Jest 的 spyOn 期间出现类型错误:无法设置只有 getter 的 #<Object> 的 属性 getRequest
TypeError during Jest's spyOn: Cannot set property getRequest of #<Object> which has only a getter
我正在使用 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 测试期间是什么原因造成的呢?我怎样才能通过这个测试?
正如评论中所建议的,jest 需要在测试对象上使用 setter,而 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
这个很有趣。
问题
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
试图用包装原始函数的间谍替换 属性 但不能' t 如果 属性 仅定义为 get
.
解决方案
不是将 ../../utils/serverRequests
(它重新导出 getRequest
)导入到测试中,而是导入定义了 getRequest
的模块并使用该模块创建间谍。
备用解决方案
按照@Volodymyr 和@TheF
的建议模拟整个 utils/serverRequests
模块
对于遇到此问题的任何其他人,您可以将 babel 设置为使用 "loose" 转换,这为我解决了这个问题。只需将它设置在你的 .babelrc 文件中
{
"presets": [
["@babel/preset-env", {
"loose": true
}]
]
}
Jest 单元测试升级失败,穿越时:
export * from './serverRequests';
直接引用文件以避免“...只有 getter”问题!
最近我们在使用的库中碰到了类似的东西。 Babel 只为从库导出的所有成员提供 getter,所以我们在测试的顶部这样做:
jest.mock('some-library', () => ({
...jest.requireActual('some-library')
}));
这解决了问题,因为它创建了一个新的 plain-old JS 对象,库中每个 属性 都有一个成员。
使用 ts-jest
作为编译器进行测试,如果您以这种方式模拟模块,它将工作:
import * as serverRequests from "./../../utils/serverRequests";
jest.mock('./../../utils/serverRequests', () => ({
__esModule: true,
...jest.requireActual('./../../utils/serverRequests')
}));
const getRequestMock = jest.spyOn(serverRequests, "getRequest");
如果您希望不修改导入,可以通过以下方式解决问题:
import * as lib from './lib'
jest.mock('./lib/subModule')
it('can be mocked', () => {
jest.spyOn(lib, 'subModuleFunction')
})
您需要添加更多 jest.mock
行以用于您希望监视的任何其他重新导出的函数。
我正在使用 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 测试期间是什么原因造成的呢?我怎样才能通过这个测试?
正如评论中所建议的,jest 需要在测试对象上使用 setter,而 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
这个很有趣。
问题
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
试图用包装原始函数的间谍替换 属性 但不能' t 如果 属性 仅定义为 get
.
解决方案
不是将 ../../utils/serverRequests
(它重新导出 getRequest
)导入到测试中,而是导入定义了 getRequest
的模块并使用该模块创建间谍。
备用解决方案
按照@Volodymyr 和@TheF
的建议模拟整个utils/serverRequests
模块
对于遇到此问题的任何其他人,您可以将 babel 设置为使用 "loose" 转换,这为我解决了这个问题。只需将它设置在你的 .babelrc 文件中
{
"presets": [
["@babel/preset-env", {
"loose": true
}]
]
}
Jest 单元测试升级失败,穿越时:
export * from './serverRequests';
直接引用文件以避免“...只有 getter”问题!
最近我们在使用的库中碰到了类似的东西。 Babel 只为从库导出的所有成员提供 getter,所以我们在测试的顶部这样做:
jest.mock('some-library', () => ({
...jest.requireActual('some-library')
}));
这解决了问题,因为它创建了一个新的 plain-old JS 对象,库中每个 属性 都有一个成员。
使用 ts-jest
作为编译器进行测试,如果您以这种方式模拟模块,它将工作:
import * as serverRequests from "./../../utils/serverRequests";
jest.mock('./../../utils/serverRequests', () => ({
__esModule: true,
...jest.requireActual('./../../utils/serverRequests')
}));
const getRequestMock = jest.spyOn(serverRequests, "getRequest");
如果您希望不修改导入,可以通过以下方式解决问题:
import * as lib from './lib'
jest.mock('./lib/subModule')
it('can be mocked', () => {
jest.spyOn(lib, 'subModuleFunction')
})
您需要添加更多 jest.mock
行以用于您希望监视的任何其他重新导出的函数。