9

I am trying to mock a method's service i export as a module from my test. This is something i use to do with "sinon", but i would like to use jest as much as possible.

This is a classic test, i have an "authentication" service and a "mailer" service.

The "authentication" service can register new users, and after each new registration, it ask the mailer service to send the new user a "welcome email".

So testing the register method of my authentication service, i would like to assert (and mock) the "send" method of the mailer service.

How to do that? Here is what i tried, but it calls the original mailer.send method:

// authentication.js

const mailer = require('./mailer');

class authentication {
  register() { // The method i am trying to test
    // ...

    mailer.send();
  }
}

const authentication = new Authentication();

module.exports = authentication;


// mailer.js

class Mailer {
  send() { // The method i am trying to mock
    // ...
  }
}

const mailer = new Mailer();

module.exports = mailer;


// authentication.test.js

const authentication = require('../../services/authentication');

describe('Service Authentication', () => {
  describe('register', () => {
    test('should send a welcome email', done => {
      co(function* () {
        try {
          jest.mock('../../services/mailer');
          const mailer = require('../../services/mailer');
          mailer.send = jest.fn( () => { // I would like this mock to be called in authentication.register()
            console.log('SEND MOCK CALLED !');
            return Promise.resolve();
          });

          yield authentication.register(knownUser);

          // expect();

          done();
        } catch(e) {
          done(e);
        }
      });
    });
  });
});
Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
Ludo
  • 5,060
  • 15
  • 53
  • 85

1 Answers1

11

First you have to mock the mailer module with a spy so you can later set. And you to let jest know about using a promise in your test, have a look at the docs for the two ways to do this.

const authentication = require('../../services/authentication');
const mailer = require('../../services/mailer');
jest.mock('../../services/mailer', () => ({send: jest.fn()})); 

describe('Service Authentication', () => {
  describe('register', () => {
    test('should send a welcome email', async() => {
          const p = Promise.resolve()
          mailer.send.mockImplementation(() =>  p) 
          authentication.register(knownUser);
          await p
          expect(mailer.send).toHaveBeenCalled;
        }
      });
    });
  });
});
Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
  • Thanks for your answer, i just would like to understand one thing before validating the answer, do you know why is it only working if we require and call jest.mock outside of the test()? Why can't i do that in a beforeEach for example (i tested, it's not working and i can't figure out why). Thanks! – Ludo May 02 '17 at 08:35
  • What exactly cant be moved to `beforEach`. – Andreas Köberle May 02 '17 at 08:44
  • beforeEach( () => { const mailer = require('../../services/mailer'); jest.mock('../../services/mailer', () => ({send: jest.fn()})); mailer.send.mockImplementation( () => Promise.resolve() ) }); – Ludo May 02 '17 at 09:11
  • Ah, the point is that you need to have the reference to the promise in your test. Either to use em for `async/await` or to return em from the test. So it should work with `beforeEach` if you store the reference to the promise and use it in the test as in my answer. – Andreas Köberle May 02 '17 at 09:18
  • It is not related to the promise but the mock function itself, if i put this code in a beforeEach() or even in the top of the test(), the original mailer.send function is invocked and not the mock. Looks like we have to require and jest.mock the mailer outside of the describe, but i can't get why it behaves differently. – Ludo May 02 '17 at 09:31
  • In my example I use `import` and the `mock ` call is automatically hoisted to the top of the file at compile time. See docs here:http://facebook.github.io/jest/docs/manual-mocks.html#using-with-es-module-imports So in your case I would assume you have to put the `jest.mock` call before your `require` statement. – Andreas Köberle May 02 '17 at 09:53