web-dev-qa-db-ja.com

Jestを使ってES6モジュールのインポートをモックするにはどうすればいいですか?

これは不可能だと思い始めていますが、とにかくお願いしたいと思います。

私のES6モジュールの1つが特定の方法で別のES6モジュールを呼び出すことをテストしたいです。 Jasmineを使えば、これはとても簡単です -

アプリコード:

// myModule.js
import dependency from './dependency';

export default (x) => {
  dependency.doSomething(x * 2);
}

そしてテストコード:

//myModule-test.js
import myModule from '../myModule';
import dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    spyOn(dependency, 'doSomething');

    myModule(2);

    expect(dependency.doSomething).toHaveBeenCalledWith(4);
  });
});

Jestと同等のものは何ですか?私はこれがやりたいことのような単純なことのように感じます、しかし私はそれを理解しようとして私の髪を引き裂いています。

一番近いのは、importrequireに置き換え、それらをテスト/関数の中に移動することです。どちらも私がやりたいことではありません。

// myModule.js
export default (x) => {
  const dependency = require('./dependency'); // yuck
  dependency.doSomething(x * 2);
}

//myModule-test.js
describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    jest.mock('../dependency');

    myModule(2);

    const dependency = require('../dependency'); // also yuck
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

嬉しいことに、dependency.jsの中の関数がデフォルトのエクスポートであるとき、私は全部がうまくいくようにしたいです。しかし、デフォルトのエクスポートをスパイしてもJasmineではうまくいかないことがわかっています(少なくともうまくいくことはあり得ませんでした)ので、Jestでもそれが可能であることを望みません。

182
Cam Jackson

私はimport *を含むハックを使うことによってこれを解決することができました。名前付きとデフォルトの両方のエクスポートでも機能します。

名前付きエクスポートの場合:

// dependency.js
export const doSomething = (y) => console.log(y)

// myModule.js
import { doSomething } from './dependency';

export default (x) => {
  doSomething(x * 2);
}

// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.doSomething = jest.fn(); // Mutate the named export

    myModule(2);

    expect(dependency.doSomething).toBeCalledWith(4);
  });
});

またはデフォルトのエクスポートの場合:

// dependency.js
export default (y) => console.log(y)

// myModule.js
import dependency from './dependency'; // Note lack of curlies

export default (x) => {
  dependency(x * 2);
}

// myModule-test.js
import myModule from '../myModule';
import * as dependency from '../dependency';

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    dependency.default = jest.fn(); // Mutate the default export

    myModule(2);

    expect(dependency.default).toBeCalledWith(4); // Assert against the default
  });
});

Mihai Damianが以下で非常に正しく指摘しているように、これはdependencyのモジュールオブジェクトを変更しているので、他のテストに「リーク」します。したがって、この方法を使用する場合は、元の値を保存してから、テストごとに元に戻す必要があります。これをJestで簡単に行うには、jest.fn()の代わりに spyOn() methodを使用します。元の値を簡単に復元できるので、前述の「リーク」を避けることができます。

166
Cam Jackson

あなたはモジュールをモックし、スパイを自分で設定しなければなりません:

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency', () => ({
  doSomething: jest.fn()
}))

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
    expect(dependency.doSomething).toBeCalledWith(4);
  });
});
116

Andreasの回答にさらに追加する私はES6コードで同じ問題を抱えていましたが、インポートを変更したくありませんでした。それはハッキーだった。だから私はこれをしました

import myModule from '../myModule';
import dependency from '../dependency';
jest.mock('../dependency');

describe('myModule', () => {
  it('calls the dependency with double the input', () => {
    myModule(2);
  });
});

そして、 "__ mocks __"フォルダにdependency.jsと並行してdependency.jsを追加しました。これは私のために働きました。また、これにより、モック実装から適切なデータを返すことができました。モックするモジュールへの正しいパスを指定してください。

37
mdsAyubi

Jestを使用してES6依存関係モジュールのデフォルトのエクスポートをモックするには:

import myModule from '../myModule';
import dependency from '../dependency';

jest.mock('../dependency');

// If necessary, you can place a mock implementation like this:
dependency.mockImplementation(() => 42);

describe('myModule', () => {
  it('calls the dependency once with double the input', () => {
    myModule(2);

    expect(dependency).toHaveBeenCalledTimes(1);
    expect(dependency).toHaveBeenCalledWith(4);
  });
});

他の選択肢は私の場合にはうまくいきませんでした。

36
falsarella

質問はすでに回答されていますが、次のように解決できます。

dependency.js

module.exports.doSomething = (x) => x

myModule.js:

const { doSomething } = require('./dependency')
module.exports = (x) => doSomething(x * 2)

myModule.spec.js:

jest.mock('../dependency')
const { doSomething } = require('../dependency')
const myModule = require('../myModule')
describe('myModule', () => {
    it('calls the dependency with double the input', () => {
      doSomething.mockImplementation((x) => x * 10)

      myModule(2);

      expect(doSomething).toHaveBeenCalledWith(4);
      console.log(myModule(2)) // 40
    });
  });
0
Slim

私はこれを別の方法で解決しました。あなたのdependency.jsがあるとしましょう

export const myFunction = () => { }

以下の内容でdepdency.mock.jsファイルを作成します。

export const mockFunction = jest.fn();

jest.mock('dependency.js', () => ({ myFunction: mockFunction }));

テストでは、使用する依存関係を持つファイルをインポートする前に、

import { mockFunction } from 'dependency.mock'
import functionThatCallsDep from './tested-code'

it('my test', () => {
    mockFunction.returnValue(false);

    functionThatCallsDep();

    expect(mockFunction).toHaveBeenCalled();

})
0
Felipe Leusin