Reputation: 3506
In my componentDidMount
of a React.Component
instance I have a fetch()
call that on response calls setState
.
I can mock out the request and respond using sinon
but I don't know when fetch will have resolved it's promise chain.
componentDidMount() {
fetch(new Request('/blah'))
.then((response) => {
setState(() => {
return newState;
};
});
}
In my test using jest
with enzyme
:
it('has new behaviour from result of set state', () => {
let component = mount(<Component />);
requests.pop().respond(200);
component.update() // fetch() has not responded yet and
// thus setState has not been called yet
// so does nothing
assertNewBehaviour(); // fails
// now setState occurs after fetch() responds sometime after
});
Do I need to flush the Promise queue/callback queue or something similar? I could do a repeated check for newBehaviour with a timeout but that's less than ideal.
Upvotes: 3
Views: 3555
Reputation: 5358
Mock it using nock
(link) and you can use almost immediately. Also there is a video tutorial (link). Don't forget to use waitFor
.
test.only('should use API response', async () => {
nock(BASE_URL)
.get(`/account/${ACCOUNT_ID}/lists?page=1`)
.reply(200, listsMockResponse1page);
const {getByTestId} = render(component);
...
Upvotes: 0
Reputation: 5166
The react testing library has a waitFor
function that works perfectly for this case scenario.
I will give an example with hooks and function as that is the current react pattern. Lets say you have a component similar to this one:
export function TestingComponent(props: Props) {
const [banners, setBanners] = useState<MyType>([]);
React.useEffect(() => {
const response = await get("/...");
setBanners(response.banners);
}, []);
return (
{banners.length > 0 ? <Component> : </NoComponent>}
);
}
Now you can write a test like this to make sure that when banners are set Component
is rendered
test("when the banner matches the url it renders", async () => {
const {container} = render(<TestingComponent />);
await waitFor(() => {expect(...).toBe(...)});
});
waitFor
will wait for the condition in the function to be met before proceeding. There is a timeout that will fail the test if the condition is not met in X time. Check out the react testing library docs for more info
Upvotes: 0
Reputation: 3751
Since you're not making any real api calls or other time-consuming operations, the asynchronous operation will resolve in a predictably short time.
You can therefore simply wait a while.
it('has new behaviour from result of set state', (done) => {
let component = mount(<Component />);
requests.pop().respond(200);
setTimeout(() => {
try {
component.update();
assertNewBehaviour();
done();
} catch (error) {
done(error);
}
}, 1000);
});
Upvotes: 0
Reputation: 3506
The best answer it seems is to be use a container pattern and pass down the API data from a container class with separated concerns and test the components separately. This allows the component under test to simply take the API data as props and makes it much more testable.
Upvotes: 1