Reputation: 6818
I have component where I perform two fetch
operations in componentDidMount
. I want to test this and I have to be honest: it's not clear to me how to proceed at all.
The point is that it seems that there isn't a "standard" way to proceed. Essentially what I find more confusing is:
It's not very clear to me when I should use one approach/library instead of the other.
This is a simplified version of my function:
componentDidMount() {
fetch(URL, {
method: 'GET',
}).then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error("Error loading data from " + URL);
}
}).then(data => {
if (!_.isEmpty(data)) {
this.setState({
data: data,
});
} else {
throw new Error("Invalid data from " + URL);
}
}).catch(error => {
console.log(URL + ' error: ', error);
this.setState({error});
});
const payload = {...};
fetch(URL2, {
method: 'POST',
body: JSON.stringify(payload),
}).then(response => {
if (response.ok) {
return response.json();
} else {
throw new Error("Error loading data from " + URL2);
}
}).then(data => {
if (!_.isEmpty(data2)) {
this.setState({
data2: data2
});
} else {
throw new Error("Invalid data from " + URL2);
}
}).catch(error => {
this.setState({error, isLoading: false});
});
}
What I want to test is:
GET
) went well, test the updated state
respect the form that I want (so both case where data
is well/bad formed).state
is updated with error
Of course I will need a mocking mechanism to mock the two answers (for GET
and POST
operations) but is not clear how should I do it, or how to test the results.
Upvotes: 5
Views: 8198
Reputation: 96
We generally test the state
that the lifecycle methods have changed by mocking the fetch calls. Avoid using setTimeout
in tests as you never know how much time the fetchMock is gonna take, so you can use await
instead of that. For example:
import React from "react";
import {shallow} from "enzyme";
import fetchMock from "fetch-mock";
import TestComponent from "./TestComponent";
describe(() => {
it("should set the state after fetching data", () => {
// arrange
fetchMock.get(`https://www.example.com`, mockResponse);
const wrapper = shallow(<TestComponent>);
// act
await wrapper.instance().componentDidMount();
// assert
expect(wrapper.state()).toEqual(newStateObject);
})
})
Upvotes: 0
Reputation: 2192
The trick here is to assert state/snapshot after the data from remote source are received. Even with mocking it's still goes asynchronously. So you can use e.g. setTimeout to postpone assertion:
import React from "react";
import { shallow } from "enzyme";
import sinon from "sinon";
import fetch from "node-fetch";
sinon.stub(fetch, "Promise").returns(
Promise.resolve({
json: () => Promise.resolve( { name: "Hello" } )
})
);
class Test extends React.Component {
state = {
name: "none"
};
async componentDidMount() {
const res = await fetch( "https://swapi.co/api/people/1" ),
data = await res.json();
this.setState({ name: data.name });
}
render() {
return <h1>{ this.state.name }</h1>;
}
}
describe( "component with fetch", () => {
test( "state gets updated with the fetch", ( done ) => {
const wrapper = shallow( <Test /> );
setTimeout(() => {
wrapper.update();
const state = wrapper.instance().state;
console.log(state);
done();
}, 10 );
});
});
Upvotes: 0
Reputation: 621
You don't need to mock the api call. fetch
has its own library tests so you don't need to test if fetch
works. But if you really need to test your method, you can just use jest
- https://facebook.github.io/jest/docs/en/asynchronous.html . Forget the jest-fetch-mock. You can test:
componentDidMount
called?yourMethod
called?yourMethod
finished, did the change occurred? (Your new state is the expected one?)Just remember not to test the libraries themselves, or to go very deep into the component tree. You should only test atomically. One thing at a time.
Now:
You can use async/await
or just test the fetch itself. First of all, you should abstract those fetch
'es to their own methods. Now. If all you do is concatenate promises and if u get everything correctly u set the state, you just need to , on the test file, resolve that promise, and on its callback, check if the state changed to what you wanted.
Again, this has all you need to know: https://facebook.github.io/jest/docs/en/asynchronous.html#promises
And if you need one more resource here you go: https://codereviewvideos.com/course/react-redux-and-redux-saga-with-symfony-3/video/testing-javascript-s-fetch-with-jest-happy-path
Upvotes: 3