web-dev-qa-db-ja.com

Jestを使用してes6クラスをモックする方法

Jestを使用してクラスMailerをモックしようとしていますが、その方法がわかりません。ドキュメントでは、これがどのように機能するかの多くの例を挙げていません。プロセスは、ノードイベント_password-reset_が発生し、そのイベントが発生したときに、Mailer.send(to, subject, body)を使用して電子メールを送信することです。私のディレクトリ構造は次のとおりです。

_project_root
-- __test__
---- server
------ services
-------- emails
---------- mailer.test.js
-- server
---- services
------ emails
-------- mailer.js
-------- __mocks__
---------- mailer.js
_

これが私の模擬ファイル___mocks__/mailer.js_です:

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

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

module.exports = Mailer;
_

と私の_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();
});
_

そして最後に私の_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();
_

それでは、Jestを使用してこのクラスを正常にモックしてテストするにはどうすればよいですか?助けてくれてありがとう!

17
dericcain

メーラークラスをモックする必要はありませんが、mailgun-jsモジュールをモックする必要があります。したがって、mailgunは、関数messagesを返す関数sendを返す関数です。そのため、モックは次のようになります。

幸せな道のために

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

エラーの場合

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

この2つのケースがあるので、テスト内でモジュールをモックするのが理にかなっています。最初に、後でケースの実装を設定できる単純なスパイでモックする必要があります。次に、モジュールをインポートする必要があります。

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

モジュールがpromiseを使用するため、テストからpromiseを返すか、async/awaitを使用するかの2つのオプションがあります。詳細については、後者を使用してください here を見てください。

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!')
});

Mailgun sendメソッドが正しいパラメーターで呼び出されたことをテストする場合は、次のようにモックを調整する必要があります。

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

これで、送信の最初のパラメーターが正しいことを確認できました。

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

Google社員と将来の訪問者のために、ES6クラスの冗談を作成する方法を以下に示します。 githubでの作業例 もあり、jestがそれらを適切にモックできるようにESモジュールの構文を変換するbabel-jestがあります。

__ mocks __/MockedClass.js

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

module.exports = () => stub;

コードはこれをnewで呼び出すことができ、テストでは関数を呼び出してデフォルトの実装を上書きできます。

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