So, I was faced with a problem. I tried to test functionality: Click on a button to invoke async. function and on the end redirect to a new page. The test below works if the invoked function is synchronous but in asynchronous case, I don't see expected changes. So, what I'm doing wrong?
Here is the function:
import React, { useState } from 'react';
import { useHistory } from 'react-router-dom';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import { ROUTE_AUTHOR_LIST } from '../../constants';
import { createAuthor } from '../../services/authors';
const AuthorCreate = () => {
const history = useHistory();
const handleSave = async () => {
const payload = ...
await createAuthor(payload);
history.push(ROUTE_AUTHOR_LIST);
};
return (
<div>
<Button variant="primary" onClick={ handleSave }>
Save
</Button>
</div>
);
}
export default AuthorCreate;
My test:
import React from "react";
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { createMemoryHistory } from "history";
import { Router } from "react-router-dom";
import { ROUTE_AUTHOR_LIST } from "../constants";
describe("Save button functionality", () => {
test("redirect to correct URL", async () => {
const history = createMemoryHistory();
render(
<Router history={history}>
<AuthorCreate />
</Router>
);
expect(
screen.getByRole("heading", { name: /create author/i })
).toBeInTheDocument();
const saveBtn = screen.getByRole("button", {
name: /save/i,
type: "button",
});
userEvent.click(saveBtn);
// NOT PROPERLY WORKS
expect(await history.length).toBe(2);
expect(await history.location.pathname).toBe(ROUTE_AUTHOR_LIST);
});
Is the asynchronous thing happening in createAuthor mocked correctly? So is your code ever reaching the history.push line in the test?
You can use waitFor (from react-testing-library) to wait for asynchronous things in test. So for example
await waitFor(() => expect(history.length).toBe(2))