I'm writing a React application with TypeScript. I do my unit tests using Jest.
I have a function that makes an API call:
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);
The getRequest
function looks like this:
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));
})
);
I wanted to try the API function using Jest like this:
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);
});
});
});
The problem is that the line with spyOn
throws the following error:
● 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)
I googled this and only found posts about hot reloading. So what could cause this during Jest test? How can I get this test to pass?
This one was interesting.
Babel
generates properties with only get
defined for re-exported functions.
utils/serverRequests/index.ts
re-exports functions from other modules so an error is thrown when jest.spyOn
is used to spy on the re-exported functions.
Given this code re-exporting everything from lib
:
export * from './lib';
...Babel
produces this:
'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];
}
});
});
Note that the properties are all defined with only get
.
Trying to use jest.spyOn
on any of those properties will generate the error you are seeing because jest.spyOn
tries to replace the property with a spy wrapping the original function but can't if the property is defined with only get
.
Instead of importing ../../utils/serverRequests
(which re-exports getRequest
) into the test, import the module where getRequest
is defined and use that module to create the spy.
Mock the entire utils/serverRequests
module as suggested by @Volodymyr and @TheF
As suggested in the comments, jest requires a setter on the tested object which es6 module objects don't have. jest.mock()
allows you solving this by mocking your required module after the import.
Try mocking the exports from your serverRequests file
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);
});
});
Here are some useful links:
https://jestjs.io/docs/en/es6-class-mocks
https://jestjs.io/docs/en/mock-functions
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With