TypeScriptでReactアプリケーションを作成しています。Jestを使用して単体テストを行っています。
API呼び出しを行う関数があります。
import { ROUTE_INT_QUESTIONS } from "../../../config/constants/routes";
import { intQuestionSchema } from "../../../config/schemas/intQuestions";
import { getRequest } from "../../utils/serverRequests";
const intQuestionListSchema = [intQuestionSchema];
export const getIntQuestionList = () => getRequest(ROUTE_INT_QUESTIONS, intQuestionListSchema);
getRequest
関数は次のようになります。
import { Schema } from "normalizr";
import { camelizeAndNormalize } from "../../core";
export const getRequest = (fullUrlRoute: string, schema: Schema) =>
fetch(fullUrlRoute).then(response =>
response.json().then(json => {
if (!response.ok) {
return Promise.reject(json);
}
return Promise.resolve(camelizeAndNormalize(json, schema));
})
);
次のように、Jestを使用してAPI関数を試してみました。
import fetch from "jest-fetch-mock";
import { ROUTE_INT_QUESTIONS } from "../../../config/constants/routes";
import {
normalizedIntQuestionListResponse as expected,
rawIntQuestionListResponse as response
} from "../../../config/fixtures";
import { intQuestionSchema } from "../../../config/schemas/intQuestions";
import * as serverRequests from "./../../utils/serverRequests";
import { getIntQuestionList } from "./intQuestions";
const intQuestionListSchema = [intQuestionSchema];
describe("getIntQuestionList", () => {
beforeEach(() => {
fetch.resetMocks();
});
it("should get the int question list", () => {
const getRequestMock = jest.spyOn(serverRequests, "getRequest");
fetch.mockResponseOnce(JSON.stringify(response));
expect.assertions(2);
return getIntQuestionList().then(res => {
expect(res).toEqual(expected);
expect(getRequestMock).toHaveBeenCalledWith(ROUTE_INT_QUESTIONS, intQuestionListSchema);
});
});
});
問題は、spyOn
を含む行が次のエラーをスローすることです。
● getRestaurantList › should get the restaurant list
TypeError: Cannot set property getRequest of #<Object> which has only a getter
17 |
18 | it("should get the restaurant list", () => {
> 19 | const getRequestMock = jest.spyOn(serverRequests, "getRequest");
| ^
20 | fetch.mockResponseOnce(JSON.stringify(response));
21 |
22 | expect.assertions(2);
at ModuleMockerClass.spyOn (node_modules/jest-mock/build/index.js:706:26)
at Object.spyOn (src/services/api/IntQuestions/intQuestions.test.ts:19:33)
私はこれをグーグルで検索し、ホットリロードに関する投稿のみを見つけました。では、Jestテスト中にこれを引き起こす原因は何でしょうか?このテストに合格するにはどうすればよいですか?
これは面白かった。
Babel
は、再エクスポートされた関数に対してget
のみが定義されたプロパティを生成します。
utils/serverRequests/index.ts
は他のモジュールから関数を再エクスポートするため、jest.spyOn
を使用して再エクスポートされた関数をスパイするとエラーがスローされます。
このコードを考えると、lib
からすべてを再エクスポートします。
export * from './lib';
...Babel
はこれを生成します:
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _lib = require('./lib');
Object.keys(_lib).forEach(function (key) {
if (key === "default" || key === "__esModule") return;
Object.defineProperty(exports, key, {
enumerable: true,
get: function get() {
return _lib[key];
}
});
});
プロパティはすべてget
のみで定義されていることに注意してください。
これらのプロパティのいずれかでjest.spyOn
を使用しようとすると、jest.spyOn
がプロパティを元の関数をラップするスパイに置き換えようとしますが、プロパティがget
のみで定義されている場合はできないため、表示されているエラーが発生します。
../../utils/serverRequests
(getRequest
を再エクスポートする)をテストにインポートする代わりに、getRequest
が定義されているモジュールをインポートし、そのモジュールを使用してスパイを作成します。
@Volodymyrと@TheFの提案に従って、utils/serverRequests
モジュール全体をモックします。
コメントで提案されているように、jestはes6モジュールオブジェクトにはないテスト済みオブジェクトのセッターを必要とします。 jest.mock()
を使用すると、インポート後に必要なモジュールをモックすることでこれを解決できます。
ServerRequestsファイルからのエクスポートをモックしてみてください
import * as serverRequests from './../../utils/serverRequests';
jest.mock('./../../utils/serverRequests', () => ({
getRequest: jest.fn()
}));
// ...
// ...
it("should get the int question list", () => {
const getRequestMock = jest.spyOn(serverRequests, "getRequest")
fetch.mockResponseOnce(JSON.stringify(response));
expect.assertions(2);
return getIntQuestionList().then(res => {
expect(res).toEqual(expected);
expect(getRequestMock).toHaveBeenCalledWith(ROUTE_INT_QUESTIONS, intQuestionListSchema);
});
});
ここにいくつかの便利なリンクがあります:
https://jestjs.io/docs/en/es6-class-mocks
https://jestjs.io/docs/en/mock-functions