I'm trying to write a test for a React component that needs to complete an asynchronous action in its componentWillMount
method. componentWillMount
calls a function, passed as a prop, which returns a promise, and I mock this function in my test.
This works fine, but if a test fails in a call to setImmediate
or process.nextTick
, the exception isn't handled by Jest and it exits prematurely. Below, you can see I even try to catch this exception, to no avail.
How can I use something like setImmediate
or nextTick
with Jest? The accepted answer to this question is what I'm trying to implement unsuccessfully: React Enzyme - Test `componentDidMount` Async Call.
it('should render with container class after getting payload', (done) => {
let resolveGetPayload;
let getPayload = function() {
return new Promise(function (resolve, reject) {
resolveGetPayload = resolve;
});
}
const enzymeWrapper = mount(<MyComponent getPayload={getPayload} />);
resolveGetPayload({
fullname: 'Alex Paterson'
});
try {
// setImmediate(() => {
process.nextTick(() => {
expect(enzymeWrapper.hasClass('container')).not.toBe(true); // Should and does fail
done();
});
} catch (e) {
console.log(e); // Never makes it here
done(e);
}
});
Jest v18.1.0
Node v6.9.1
1. process. nextTick() is used to schedule a callback function to be invoked in the next iteration of the Event Loop. setImmediate() method is used to execute a function right after the current event loop finishes.
nextTick. A method of the native Node process module, process. nextTick is similar to the familiar setTimeout method in which it delays execution of its callback function until some point in the future.
If you noticed that Jest (npm / yarn) test hang at the end of the test execution while you run tests in parallel on your CI server with @knapsack-pro/jest client then you probably have open handles preventing Jest from exiting cleanly. Another tip: you can also verify if you have enough memory on CI server.
Wrapping the callback block passed to process.nextTick
or setImmediate
in a try
/catch
works, as others have shown, but this is verbose and distracting.
A cleaner approach is to flush promises using the brief line await new Promise(setImmediate);
inside an async
test callback. Here's a working example of using this to let an HTTP request in a useEffect
(equally useful for componentDidMount
) resolve and trigger a re-render before running assertions:
LatestGist.js
):import axios from "axios";
import React, {useState, useEffect} from "react";
export default () => {
const [gists, setGists] = useState([]);
const getGists = async () => {
const res = await axios.get("https://api.github.com/gists");
setGists(res.data);
};
useEffect(() => {
getGists();
}, []);
return (
<>
{gists.length
? <div data-test="test-latest-gist">
the latest gist was made on {gists[0].created_at}
by {gists[0].owner.login}
</div>
: <div>loading...</div>}
</>
);
};
LatestGist.test.js
):import React from "react";
import {act} from "react-dom/test-utils";
import Enzyme, {mount} from "enzyme";
import Adapter from "enzyme-adapter-react-16";
Enzyme.configure({adapter: new Adapter()});
import mockAxios from "axios";
import LatestGist from "../src/components/LatestGist";
jest.mock("axios");
describe("LatestGist", () => {
beforeEach(() => jest.resetAllMocks());
it("should load the latest gist", async () => {
mockAxios.get.mockImplementationOnce(() =>
Promise.resolve({
data: [
{
owner: {login: "test name"},
created_at: "some date"
}
],
status: 200
})
);
const wrapper = mount(<LatestGist />);
let gist = wrapper
.find('[data-test="test-latest-gist"]')
.hostNodes()
;
expect(gist.exists()).toBe(false);
await act(() => new Promise(setImmediate));
wrapper.update();
expect(mockAxios.get).toHaveBeenCalledTimes(1);
gist = wrapper
.find('[data-test="test-latest-gist"]')
.hostNodes()
;
expect(gist.exists()).toBe(true);
expect(gist.text()).toContain("test name");
expect(gist.text()).toContain("some date");
});
});
Forcing a failed assertion with a line like expect(gist.text()).toContain("foobar");
doesn't cause the suite to crash:
● LatestGist › should load the latest gist
expect(string).toContain(value)
Expected string:
"the latest gist was made on some date by test name"
To contain value:
"foobar"
at Object.it (src/LatestGist.test.js:30:25)
Here are my dependencies:
{
"dependencies": {
"axios": "^0.18.0",
"react": "^16.8.6",
"react-dom": "^16.8.6"
},
"devDependencies": {
"enzyme": "3.9.0",
"enzyme-adapter-react-16": "1.12.1",
"jest": "24.7.1",
"jest-environment-jsdom": "24.7.1"
}
}
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