create-app-component を使用してプロジェクトを作成します。ビルドスクリプト(babel、webpack、jest)を使用して新しいアプリを構成します。
私がテストしようとしているReactコンポーネント。コンポーネントは関数を公開する別のJavaScriptファイルを必要としています。
私のsearch.jsファイル
export {
search,
}
function search(){
// does things
return Promise.resolve('foo')
}
私の反応コンポーネント:
import React from 'react'
import { search } from './search.js'
import SearchResults from './SearchResults'
export default SearchContainer {
constructor(){
this.state = {
query: "hello world"
}
}
componentDidMount(){
search(this.state.query)
.then(result => { this.setState({ result, })})
}
render() {
return <SearchResults
result={this.state.result}
/>
}
}
単体テストでは、メソッドsearch
が正しい引数で呼び出されたことを確認します。
私のテストは次のようになります。
import React from 'react';
import { shallow } from 'enzyme';
import should from 'should/as-function';
import SearchResults from './SearchResults';
let mockPromise;
jest.mock('./search.js', () => {
return { search: jest.fn(() => mockPromise)};
});
import SearchContainer from './SearchContainer';
describe('<SearchContainer />', () => {
it('should call the search module', () => {
const result = { foo: 'bar' }
mockPromise = Promise.resolve(result);
const wrapper = shallow(<SearchContainer />);
wrapper.instance().componentDidMount();
mockPromise.then(() => {
const searchResults = wrapper.find(SearchResults).first();
should(searchResults.prop('result')).equal(result);
})
})
});
変数の前にmock
を付ける必要があるため、jest.mock
を機能させる方法を見つけるのに苦労しました。
ただし、メソッドsearch
の引数をテストする場合は、テストでモック関数を使用可能にする必要があります。
モック部分を変換して、変数を使用する場合:
const mockSearch = jest.fn(() => mockPromise)
jest.mock('./search.js', () => {
return { search: mockSearch};
});
私はこのエラーを受け取ります:
TypeError:(0、_search.search)は関数ではありません
jest.fn
にアクセスして引数をテストしようとしても、機能させることはできません。
何が間違っていますか?
このエラーが発生する理由は、さまざまな操作がホイストされる方法に関係しています。
元のコードでは、SearchContainer
afterのみをインポートし、mockSearch
に値を割り当て、jestのmock
、 specs は次のことを示します:Before instantiating a module, all of the modules it requested must be available
。
したがって、SearchContainer
がインポートされた時点で、search
をインポートしても、mockSearch
変数は未定義のままです。
search.js
がまだモックされていないことを暗示しているように見えるため、この奇妙なことに気付くかもしれません。したがって、モックはまったく機能しません。幸いなことに、(babel-)jestはmock
および同様の関数への呼び出しをインポートよりも高くhigherするので、モックは作業。
それにもかかわらず、モックの関数によって参照されるmockSearch
の割り当ては、mock
呼び出しで引き上げられません。したがって、関連する操作の順序は次のようになります。
./search.js
の模擬ファクトリーを設定しますmockSearch
に値を割り当てますステップ2が発生すると、コンポーネントに渡されるsearch
関数は未定義になり、ステップ3での割り当てはそれを変更するには遅すぎます。
mock
呼び出しの一部としてモック関数を作成すると(ホイストされるように)、初期の例で示したように、コンポーネントモジュールによってインポートされたときに有効な値になります。
先ほど指摘したように、テストでモックされた関数を使用可能にしたいときに問題が始まります。これには明らかな解決策が1つあります。既にモックしたモジュールを個別にインポートします。
インポートの前に冗談のモックが実際に行われることがわかっているので、簡単なアプローチは次のとおりです。
import { search } from './search.js'; // This will actually be the mock
jest.mock('./search.js', () => {
return { search: jest.fn(() => mockPromise) };
});
[...]
beforeEach(() => {
search.mockClear();
});
it('should call the search module', () => {
[...]
expect(search.mock.calls.length).toBe(1);
expect(search.mock.calls[0]).toEqual(expectedArgs);
});
実際、次のものを置き換えることができます。
import { search } from './search.js';
と:
const { search } = require.requireMock('./search.js');
これは機能的な違いをもたらすべきではありませんが、あなたがやっていることをもう少し明示的にするかもしれません(そしてフローなどの型チェックシステムを使用している人を助けるはずですので、モック関数を呼び出そうとしているとは思わない元のsearch
)。
これらすべては、モックする必要があるのがモジュール自体のデフォルトのエクスポートである場合にのみ厳密に必要です。それ以外の場合(@publicJornが指摘しているように)、テストで特定の関連メンバーを次のように再割り当てできます。
import * as search from './search.js';
beforeEach(() => {
search.search = jest.fn(() => mockPromise);
});
応答でapi呼び出しをモックするときは、async()=>テストを忘れずに、ラッパーの更新を待ってください。私のページは、典型的なcomponentDidMount => API呼び出しを行いました.
...otherImports etc...
const SomeApi = require.requireMock('../../api/someApi.js');
jest.mock('../../api/someApi.js', () => {
return {
GetSomeData: jest.fn()
};
});
beforeEach(() => {
// Clear any calls to the mocks.
SomeApi.GetSomeData.mockClear();
});
it("renders valid token", async () => {
const responseValue = {data:{
tokenIsValid:true}};
SomeApi.GetSomeData.mockResolvedValue(responseValue);
let wrapper = shallow(<MyPage {...props} />);
expect(wrapper).not.toBeNull();
await wrapper.update();
const state = wrapper.instance().state;
expect(state.tokenIsValid).toBe(true);
});