web-dev-qa-db-ja.com

JestのspyOn中のTypeError:ゲッターのみを持つ#<Object>のプロパティgetRequestを設定できません

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テスト中にこれを引き起こす原因は何でしょうか?このテストに合格するにはどうすればよいですか?

19
J. Hesters

これは面白かった。

問題

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/serverRequestsgetRequestを再エクスポートする)をテストにインポートする代わりに、getRequestが定義されているモジュールをインポートし、そのモジュールを使用してスパイを作成します。

代替ソリューション

@Volodymyrと@TheFの提案に従って、utils/serverRequestsモジュール全体をモックします。

23
Brian Adams

コメントで提案されているように、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

4
The F