web-dev-qa-db-fra.com

Comment se moquer de la classe es6 en utilisant Jest

J'essaie de me moquer d'une classe Mailer en utilisant jest et je ne peux pas comprendre comment le faire. Les documents ne donnent pas beaucoup d'exemples de la façon dont cela fonctionne. Le processus consiste à avoir un événement de nœud password-reset Qui est déclenché et lorsque cet événement est déclenché, je veux envoyer un e-mail à l'aide de Mailer.send(to, subject, body). Voici ma structure de répertoires:

project_root
-- __test__
---- server
------ services
-------- emails
---------- mailer.test.js
-- server
---- services
------ emails
-------- mailer.js
-------- __mocks__
---------- mailer.js

Voici mon faux fichier __mocks__/mailer.js:

const Mailer = jest.genMockFromModule('Mailer');

function send(to, subject, body) {
  return { to, subject, body };
}

module.exports = Mailer;

et mon mailer.test.js

const EventEmitter = require('events');
const Mailer = jest.mock('../../../../server/services/emails/mailer');

test('sends an email when the password-reset event is fired', () => {
  const send = Mailer.send();
  const event = new EventEmitter();
  event.emit('password-reset');
  expect(send).toHaveBeenCalled();
});

et enfin ma classe mailer.js:

class Mailer {

  constructor() {
    this.mailgun = require('mailgun-js')({
      apiKey: process.env.MAILGUN_API_KEY,
      domain: process.env.MAILGUN_DOMAIN,
    });
  }

  send(to, subject, body) {
    return new Promise((reject, resolve) => {
      this.mailgun.messages().send({
        from: 'Securely App <[email protected]>',
        to,
        subject: subject,
        html: body,
      }, (error, body) => {
        if (error) {
          return reject(error);
        }

        return resolve('The email was sent successfully!');
      });
    });
  }

}

module.exports = new Mailer();

Alors, comment réussir à simuler et tester cette classe, en utilisant Jest? Merci beaucoup pour votre aide!

17
dericcain

Vous n'avez pas à vous moquer de votre classe de mailer mais le mailgun-js module. Donc, mailgun est une fonction qui retourne la fonction messages qui retourne la fonction send. Ainsi, la maquette ressemblera à ceci.

pour le chemin heureux

const happyPath = () => ({
  messages: () => ({
    send: (args, callback) => callback()
  })
})

pour le cas d'erreur

const errorCase = () => ({
  messages: () => ({
    send: (args, callback) => callback('someError')
  })
})

comme vous avez ces 2 cas, il est logique de se moquer du module dans votre test. Vous devez d'abord vous en moquer avec un simple espion où nous pourrons ensuite définir l'implémentation de nos cas, puis nous devons importer le module.

jest.mock('mailgun-js', jest.fn())
import mailgun from 'mailgun-js'
import Mailer from '../../../../server/services/emails/mailer'

Comme votre module utilise des promesses, nous avons 2 options: renvoyer la promesse du test ou utiliser async/await. J'utilise le dernier pour plus d'informations jetez un oeil ici .

test('test the happy path', async() => {
 //mock the mailgun so it returns our happy path mock
  mailgun.mockImplementation(() => happyPath)
  //we need to use async/awit here to let jest recognize the promise
  const send = await Mailer.send();
  expect(send).toBe('The email was sent successfully!')
});

Si vous souhaitez tester que la méthode mailgun send a été appelée avec le paramètre correct, vous devez adapter la maquette comme ceci:

const send = jest.fn((args, callback) => callback())
const happyPath = () => ({
  messages: () => ({
    send: send
  })
})

Vous pouvez maintenant vérifier que le premier paramètre d'envoi était correct:

expect(send.mock.calls[0][0]).toMatchSnapshot()
19

Juste pour les Googleurs et les futurs visiteurs, voici comment j'ai configuré la moquerie pour les classes ES6. J'ai aussi un exemple de travail sur github , avec babel-jest pour transpiler la syntaxe du module ES afin que jest puisse les railler correctement.

__ mocks __/MockedClass.js

const stub = {
  someMethod: jest.fn(),
  someAttribute: true
}

module.exports = () => stub;

Votre code peut appeler cela avec new, et dans vos tests, vous pouvez appeler la fonction et écraser toute implémentation par défaut.

example.spec.js

const mockedClass = require("path/to/MockedClass")(); 
const AnotherClass = require("path/to/AnotherClass");
let anotherClass;

jest.mock("path/to/MockedClass");

describe("AnotherClass", () => {
  beforeEach(() => {
    mockedClass.someMethod.mockImplementation(() => {
      return { "foo": "bar" };
    });

    anotherClass = new AnotherClass();
  });

  describe("on init", () => {
    beforeEach(() => { 
      anotherClass.init(); 
    });

    it("uses a mock", () => {
      expect(mockedClass.someMethod.toHaveBeenCalled();
      expect(anotherClass.settings)
        .toEqual(expect.objectContaining({ "foo": "bar" }));
    });
  });

});
7
Justus Romijn