Search code examples
node.jsnestjs

How to make the nodemailer mocking object or factory in nestjs test code?


I am using nodemailer in my NestJS application to send emails. To prevent real emails from being sent whenever I run tests, I want to create a mocking object of nodemailer that I can use in my tests. However, I am not sure how to create this object properly.

When I tried to make a mocking object, there is "connect ECONNREFUSED 127.0.0.1:587". This error is that "nodemailer needs an access connect to gmail". So it added the setting values using docenv code.

dotenv.config({
  path: path.resolve(`src/config/env/.development.env`),
});

After that, test is success and it always send the verification mail.But I don't want to send emails when running test and want to use mocking object.

I have uploaded the entire code to CodeSandBox except .env, so I hope it is helpful for reference.

Source code (codeSandbox) : https://codesandbox.io/p/sandbox/crazy-bouman-2tvq79

UserService test code : https://codesandbox.io/p/sandbox/crazy-bouman-2tvq79?file=%2Fsrc%2Fusers%2Fusers.service.spec.ts&selection=%5B%7B%22endColumn%22%3A4%2C%22endLineNumber%22%3A26%2C%22startColumn%22%3A4%2C%22startLineNumber%22%3A26%7D%5D

EmailService : https://codesandbox.io/p/sandbox/crazy-bouman-2tvq79?file=%2Fsrc%2Femail%2Femail.service.ts&selection=%5B%7B%22endColumn%22%3A4%2C%22endLineNumber%22%3A26%2C%22startColumn%22%3A4%2C%22startLineNumber%22%3A26%7D%5D


Solution

  • You can mock your EmailService from your UsersService. Also, if you want to test EmailService you can either use jest.mock(), or you inject nodemailer as a dependency to your service, and then you will be able to mock it easily by using Nest's testing module.

    Mocking EmailService in UsersService

    describe('UsersService', () => {
      let module: TestingModule;
      let service: UsersService;
      let sendUserVerficationEmailMock;
    
      beforeAll(() => {
        dotenv.config({
          path: path.resolve(`src/config/env/.development.env`),
        });
      });
    
      beforeEach(async () => {
        sendUserVerficationEmailMock = jest
         .fn()
         .mockImplementation(() => console.log('email sent'));
    
    
        module = await Test.createTestingModule({
          imports: [
            TypeOrmModule.forRoot({
              type: 'sqlite',
              database: ':memory:',
              entities: [UserEntity],
              synchronize: true,
            }),
            UsersModule,
          ],
        })
          .overrideProvider(EmailService)
          .useValue({
            sendUserVerficationEmail: sendUserVerficationEmailMock,
          })
          .compile();
    
        service = module.get(UsersService);
        await service.createUser('tom', '[email protected]', '!@#$');
      });
    
      it('should throw Error when email is existed', async () => {
        const result = service.createUser('tom', '[email protected]', '!@#$');
        expect(sendUserVerficationEmailMock).toBeCalledTimes(1);
        await expect(result).rejects.toThrowError(
          new UnprocessableEntityException('이미 가입된 이메일 입니다.'),
        );
      });
    });
    

    Mocking NodeMailer in EmailService

    There are many examples online on how to mock third-party libraries with jest. You can try the different approaches mentioned here: https://jestjs.io/docs/jest-object.

    Or even better (IMO), is to inject NodeMailer into your service and follow the same approach as we did in the previous example

    You may also want to consider using a NestJS wrapper module for NodeMailer, something like NestJS Mailer as it offers these dependencies as NestJS modules and providers which are easier to mock.