I've searched high and low for best practices when using testing react library. I have a test which uses react context which I'm trying to test with a component to ensure it updates correct. It's quite a bit of code so I'll narrow it down here (though still a lot).
Working version of this code can be found here https://codesandbox.io/s/react-playground-forked-mllwv8 (though the test platform isn't set up so this may not help much as not sure how to implement webpack on cs)
The run of functionality is:
- PeoplePageComponent loads
- useEffect runs and runs fetchPeople from the CONTEXT
- fetchPeople(context function) runs fetchPeople(actual function) from fetchPeople.jsx which dispatches SET_PEOPLE
- in PeoplePageComponent useEffect is triggered to where logic setsCurrentPage
I want to test:
- render PeoplePageComponent with providerValue (people: null)
- Assert page is
loading - assert
mockFetchPeoplewas called 1 time - Rerender PeoplePageComponent with providerValue (people:[{name: "tommy", age: 24}]) (THIS IS THE BIT I'M UNSURE OF)
- assert
mockSetCurrentPagewas called 1 time - assert page is
hasPeople
What would be the best way to do this given the set up I have? please bare in mind this code is actually very different to the code I'm using but the mechanisms are used in the same way
fetchPeople.jsx
export const fetchPeople = (dispatch) => {
// faking a request here for the sake of demonstration
const people = [
{
name: "Tommy",
age: 24
}
];
setTimeout(() => {
dispatch({
type: "SET_PEOPLE",
payload: people
});
}, 5000);
};
PeoplePageComponent.jsx
import React, { useContext, useEffect } from "react";
import { MyContext } from "./MyProvider";
export const PeoplePageComponent = () => {
const { currentPage, people, setPage, fetchPeople } = useContext(MyContext);
useEffect(() => {
if (!people) return;
setPage(people.length > 0 ? "hasPeople" : "noPeople");
}, [people]);
useEffect(() => {
fetchPeople();
}, []);
return (
<>
<p>
<b>Page:</b> {currentPage}
</p>
<p>
<b>People:</b> {JSON.stringify(people)}
</p>
{currentPage === "loading" && <h1>This is the loading page</h1>}
{currentPage === "noPeople" && <h1>This is the noPeople page</h1>}
{currentPage === "hasPeople" && <h1>This is the hasPeople page</h1>}
</>
);
};
MyProvider.jsx
import { createContext, useReducer } from "react";
import { fetchPeople } from "./fetchPeople";
const reducer = (state, action) => {
switch (action.type) {
case "SET_PAGE":
return {
...state,
currentPage: action.payload
};
case "SET_PEOPLE":
return {
...state,
people: action.payload
};
default:
return state;
}
};
const initialState = {
currentPage: "loading",
people: null
};
export const MyContext = createContext({
setPage: () => {},
setPeople: () => {},
fetchPeople: () => {},
currentPage: "loading",
people: null
});
export const MyProvider = ({ children }) => {
const [state, dispatch] = useReducer(reducer, initialState);
const providerValue = {
setPage: (page) => dispatch({ type: "SET_PAGE", payload: page }),
setPeople: (people) => dispatch({ type: "SET_PEOPLE", payload: people }),
fetchPeople: () => fetchPeople(dispatch),
currentPage: state.currentPage,
people: state.people
};
return (
<MyContext.Provider value={providerValue}>{children}</MyContext.Provider>
);
};
index.js
import React from "react";
import ReactDOM from "react-dom";
import { MyProvider } from "../src/MyProvider";
import { PeoplePageComponent } from "../src/PeoplePageComponent";
const App = () => {
return (
<MyProvider>
<main>
<PeoplePageComponent />
</main>
</MyProvider>
);
};
ReactDOM.render(<App />, document.getElementById("container"));
PeoplePageComponent.test.jsx
import { render } from "@testing-library/react";
import { PagePeopleComponent } from "../PeoplePageComponent";
import { MyContext } from "../MyProvider";
const mockedContextValue = {
setPage: jest.fn(),
setPeople: jest.fn(),
currentPage: "loading",
people: null
};
test("sets page correctly", () => {
const contextValue = {
...mockedContextValue
};
const { rerender, container } = render(
<MyContext.Provider value={contextValue}>
<PagePeopleComponent />
</MyContext.Provider>
);
expect(container).toHaveTextContent(/This is the loading page/i);
rerender(
<MyContext.Provider value={{ ...contextValue, nominees: [] }}>
<PagePeopleComponent />
</MyContext.Provider>
);
expect(container).toHaveTextContent(/This is the hasPeople page/i);
});