nestjs / nest

A progressive Node.js framework for building efficient, scalable, and enterprise-grade server-side applications with TypeScript/JavaScript 🚀
https://nestjs.com
MIT License
67.69k stars 7.63k forks source link

Love CQRS, Need testing documentation #274

Closed patrickhousley closed 6 years ago

patrickhousley commented 6 years ago

I'm submitting a...


[ ] Regression 
[ ] Bug report
[ ] Feature request
[X] Documentation issue or request
[ ] Support request => Please do not submit support request here, instead post your question on Stack Overflow.

Current behavior

No documentation exists around unit or e2e testing the cqrs module.

Expected behavior

Please provide some examples for the various patterns supported by the cqrs module.

Minimal reproduction of the problem with instructions

N/A

What is the motivation / use case for changing the behavior?

Make the documentation as good as the library is.

Environment


Nest version: 4.4.1


For Tooling issues:
- Node version: 8.9.1  
- Platform: MAC 

Others:

patrickhousley commented 6 years ago

I have been playing with testing an event handler and below is what I have so far.

import { Test } from '@nestjs/testing';
import { EventBus } from '@nestjs/cqrs';
import { rand } from '@buffalo/common/src/test-lib/random';
import { SendEmailHandler } from './send-email.handler';
import { EmailService } from '../../email.service';
import { SendEmailEvent } from '../impl/send-email.event';
import { MessageTransmissionMethod } from '../../../queue/queue.interface';
import { ProcessedEvent } from '../../../queue/events/impl/processed.event';

// Disable log outputs
jest.mock('@nestjs/common/services/logger.service');

describe('UNIT: SendEmailHandler', () => {
  let eventBus$: EventBus;
  let emailService: EmailService;
  let sendEmailHandler: SendEmailHandler;

  beforeEach(async () => {
    const module = await Test.createTestingModule({
      components: [
        SendEmailHandler,
        {
          provide: EventBus,
          useFactory: () => jest.fn()
        },
        {
          provide: EmailService,
          useFactory: () => jest.fn()
        }
      ]
    }).compile();

    eventBus$ = module.get<EventBus>(
      EventBus
    );
    emailService = module.get<EmailService>(EmailService);
    sendEmailHandler = module.get<SendEmailHandler>(SendEmailHandler);
  });

  it('should be defined', () => {
    expect(eventBus$).toBeDefined();
    expect(emailService).toBeDefined();
    expect(sendEmailHandler).toBeDefined();
  });

  it('should send the email using the EmailService', async () => {
    emailService.sendEmail = jest.fn(() => Promise.resolve());
    eventBus$.publish = jest.fn(() => { /* Do Nothing */ })
    await sendEmailHandler.handle(
      new SendEmailEvent(
        {
          MessageId: rand.n(rand.letter, 10).join('')
        },
        rand.n(rand.letter, 10).join(''),
        rand.n(rand.letter, 10).join(''),
        rand.n(rand.letter, 10).join(''),
        {},
        MessageTransmissionMethod.EMAIL
      )
    );
    expect(emailService.sendEmail).toHaveBeenCalledTimes(1);
  });

  it('should publish a new ProcessedEvent to the event bus', async () => {
    const event = new SendEmailEvent(
      {
        MessageId: rand.n(rand.letter, 10).join('')
      },
      rand.n(rand.letter, 10).join(''),
      rand.n(rand.letter, 10).join(''),
      rand.n(rand.letter, 10).join(''),
      {},
      MessageTransmissionMethod.EMAIL
    );
    emailService.sendEmail = jest.fn(() => Promise.resolve());
    eventBus$.publish = jest.fn(() => { /* Do Nothing */ });
    await sendEmailHandler.handle(event);
    expect(emailService.sendEmail).toHaveBeenCalledTimes(1);
    expect(eventBus$.publish).toHaveBeenCalledTimes(1);
    expect(eventBus$.publish).toHaveBeenCalledWith(
      new ProcessedEvent(
        event.sqsMessage,
        event.app,
        event.recepient,
        event.template,
        event.templateData,
        event.transmissionMethod
      )
    );
  });

  it('should not publish a new ProcessedEvent to the event bus if sending the email fails', async () => {
    emailService.sendEmail = jest.fn(() => Promise.reject(new Error('Test Error')));
    eventBus$.publish = jest.fn(() => { /* Do Nothing */ });
    await sendEmailHandler.handle(
      new SendEmailEvent(
        {
          MessageId: rand.n(rand.letter, 10).join('')
        },
        rand.n(rand.letter, 10).join(''),
        rand.n(rand.letter, 10).join(''),
        rand.n(rand.letter, 10).join(''),
        {},
        MessageTransmissionMethod.EMAIL
      )
    );
    expect(emailService.sendEmail).toHaveBeenCalledTimes(1);
    expect(eventBus$.publish).toHaveBeenCalledTimes(0);
  });
});
patrickhousley commented 6 years ago

I am having a harder time getting the E2E testing. When I add the CQRSModule to the testing module, Nest can't resolve dependencies of the EmailService (?, +, +, +). Please verify whether [0] argument is available in the current context. That first dependency is EventBus.

patrickhousley commented 6 years ago

After some sleep, finally figured out what I was doing wrong. Although the error was a little misleading, I figured out that I forgot to override all the components of the modules my EmailModule imported.

const module = await Test.createTestingModule({
      modules: [EmailModule]
    })
      // CQRSModule Overrides
      .overrideComponent(EventBus)
      .useValue({
        setModuleRef: jest.fn(),
        register: jest.fn(),
        publish: jest.fn()
      })
      // SharedModule Overrides
      .overrideComponent(constants.EMAIL_SOURCE_ADDRESS)
      .useValue(rand.n(rand.letter, 10).join(''))
      .overrideComponent(constants.EMAIL_REPLY_ADDRESS)
      .useValue(rand.n(rand.letter, 10).join(''))
      // TemplateModule Overrides
      .overrideComponent(constants.AWS_S3_SERVICE)
      .useValue(jest.fn())
      .overrideComponent(TemplateService)
      .useValue(jest.fn())
      // EmailModule Overrides
      .overrideComponent(constants.AWS_SES_SERVICE)
      .useValue(jest.fn())
      .compile();
patrickhousley commented 6 years ago

Closing since I got this figured out.

lock[bot] commented 5 years ago

This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.