web-dev-qa-db-ja.com

Jestを使用したEs6クラスのモック

Jestを使用して、パラメーターを受け取るコンストラクターでES6クラスをモックし、クラスのさまざまなクラス関数をモックしてテストを続行しようとしています。

問題は、この問題へのアプローチ方法に関するドキュメントが見つからないことです。私はすでに この投稿 を見ましたが、実際にはOPがクラスをモックする必要さえなかったので、私の問題を解決しません!この投稿のもう1つの答えもまったく詳しくありません。オンラインのドキュメントを指し示すものではなく、単なるコードのブロックであるため、再現可能な知識にはつながりません。

だから私は次のクラスを持っていると言います:

//socket.js;

module.exports = class Socket extends EventEmitter {

    constructor(id, password) {
        super();

        this.id = id;
        this.password = password;

        this.state = constants.socket.INITIALIZING;
    }

    connect() {
        // Well this connects and so on...
    } 

};

//__tests__/socket.js

jest.mock('./../socket');
const Socket = require('./../socket');
const socket = new Socket(1, 'password');

expect(Socket).toHaveBeenCalledTimes(1);

socket.connect()
expect(Socket.mock.calls[0][1]).toBe(1);
expect(Socket.mock.calls[0][2]).toBe('password');

明らかなように、私がモックしようとしている方法Socketとクラス関数connectそれは間違っていますが、私はそうする正しい方法を見つけることができません。

あなたの答えで、あなたがこれをモックするために行う論理的なステップと、それぞれがなぜ必要なのかを説明してください。可能であれば、Jestの公式ドキュメントへの外部リンクを提供してください!

助けてくれてありがとう!

19
SpiXel

更新:

これらすべての情報などが、新しいガイド「 ES6 Class Mocks 」でJestドキュメントに追加されました。

完全開示:私が書いた。 :-)


ES6クラスをモックする鍵は、ES6クラスが関数であることを知ることです。したがって、モックも関数でなければなりません

  1. jest.mock('./mocked-class.js');を呼び出し、「./ mocked-class.js」もインポートします。
  2. 呼び出しを追跡するクラスメソッドについては、次のようなモック関数を指す変数を作成します:const mockedMethod = jest.fn();。次のステップでそれらを使用します。
  3. MockedClass.mockImplementation()を呼び出します。モックされたメソッドを含むオブジェクトを返す矢印関数を渡します。各メソッドは、独自のモック関数(手順2で作成)に設定されます。
  4. 手動のモック(__mocks__フォルダー)を使用してES6クラスをモックするのと同じことができます。この場合、エクスポートされたモックは、上記の(3)で説明したのと同じ引数で、jest.fn().mockImplementation()を呼び出すことによって作成されます。これにより、モック関数が作成されます。この場合、スパイしたい模擬メソッドもエクスポートする必要があります。
  5. 同じことは、jest.mock('mocked-class.js', factoryFunction)を呼び出すことで実行できます。ここで、factoryFunctionは、上記の3と4で渡された同じ引数です。

例は千の言葉に値するので、ここにコードがあります。また、このすべてを示すリポジトリがあります: https://github.com/jonathan-stone/jest-es6-classes-demo/tree/mocks-working

まず、あなたのコードのために

次のセットアップコードを追加する場合、テストに合格する必要があります。

const connectMock = jest.fn(); // Lets you check if `connect()` was called, if you want

Socket.mockImplementation(() => {
    return {
      connect: connectMock
    };
  });

(注:コード内でSocket.mock.calls[0][1][0][0]、および[0][2][0][1]。 )

次に、不自然な例

いくつかの説明をインラインで。

mocked-class.js。このコードはテスト中に呼び出されることはありません。

export default class MockedClass {
  constructor() {
    console.log('Constructed');
  }

  mockedMethod() {
    console.log('Called mockedMethod');
  }
}

mocked-class-consumer.jsこのクラスは、モックされたクラスを使用してオブジェクトを作成します。本物ではなく、モックバージョンを作成してほしい。

import MockedClass from './mocked-class';

export default class MockedClassConsumer {
  constructor() {
    this.mockedClassInstance = new MockedClass('yo');
    this.mockedClassInstance.mockedMethod('bro');
  }
}

mocked-class-consumer.test.js-テスト:

import MockedClassConsumer from './mocked-class-consumer';
import MockedClass from './mocked-class';

jest.mock('./mocked-class'); // Mocks the function that creates the class; replaces it with a function that returns undefined.

// console.log(MockedClass()); // logs 'undefined'

let mockedClassConsumer;
const mockedMethodImpl = jest.fn();

beforeAll(() => {
  MockedClass.mockImplementation(() => {
    // Replace the class-creation method with this mock version.
    return {
      mockedMethod: mockedMethodImpl // Populate the method with a reference to a mock created with jest.fn().
    };
  });
});

beforeEach(() => {
  MockedClass.mockClear();
  mockedMethodImpl.mockClear();
});

it('The MockedClassConsumer instance can be created', () => {
  const mockedClassConsumer = new MockedClassConsumer();
  // console.log(MockedClass()); // logs a jest-created object with a mockedMethod: property, because the mockImplementation has been set now.
  expect(mockedClassConsumer).toBeTruthy();
});

it('We can check if the consumer called the class constructor', () => {
  expect(MockedClass).not.toHaveBeenCalled(); // Ensure our mockClear() is clearing out previous calls to the constructor
  const mockedClassConsumer = new MockedClassConsumer();
  expect(MockedClass).toHaveBeenCalled(); // Constructor has been called
  expect(MockedClass.mock.calls[0][0]).toEqual('yo'); // ... with the string 'yo'
});

it('We can check if the consumer called a method on the class instance', () => {
  const mockedClassConsumer = new MockedClassConsumer();
  expect(mockedMethodImpl).toHaveBeenCalledWith('bro'); 
// Checking for method call using the stored reference to the mock function
// It would be Nice if there were a way to do this directly from MockedClass.mock
});
21
stone