I want to test my react component when state is updated based on multiple REST calls response.
This is my code.
setuptests.js
import Enzyme from 'enzyme';
import EnzymeAdapter from '@wojtekmaj/enzyme-adapter-react-17';
Enzyme.configure({ adapter: new EnzymeAdapter() });
apiProvider.js:
const BASE_URL = "https://jsonplaceholder.typicode.com";
const get = (url) => {
return fetch(`${BASE_URL}/${url}`, {
method: "GET"
})
.then((response) => {
if (response.ok) {
return response.json();
} else {
throw new Error("Something went wrong");
}
})
.then((data) => {
return data;
})
.catch(() => {
const response = { ok: false };
return response;
});
};
export const apiProvider = { get };
apiService.js:
import { apiProvider } from "./apiProvider";
const getData1 = () => {
return apiProvider.get("todos/1");
};
const getData2 = () => {
return apiProvider.get("todos/2");
};
export const apiService = {
getData1,
getData2,
};
App.js code:
import React, { useState, useEffect } from "react";
import "./App.css";
import logo from "./logo.svg";
import { apiService } from "./apiService";
function App() {
const [isLoading, setIsLoading] = useState(true);
const [data1, setData1] = useState([]);
const [data2, setData2] = useState([]);
const [hasData1Loaded, setHasData1Loaded] = useState(false);
const [hasData2Loaded, setHasData2Loaded] = useState(false);
useEffect(() => {
if (hasData1Loaded && hasData2Loaded) {
setIsLoading(false);
}
}, [hasData1Loaded, hasData2Loaded]);
useEffect(() => {
apiService.getData1().then((response) => {
setData1(response);
setHasData1Loaded(true);
});
}, []);
useEffect(() => {
apiService.getData2().then((response) => {
setData2(response);
setHasData2Loaded(true);
});
}, []);
return (
<div className="App">
{isLoading && <div>Loading....</div>}
{!isLoading && <header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>}
</div>
);
}
export default App;
App.test.js:
import React from "react";
import { mount } from "enzyme";
import App from "./App";
import { act } from "react-dom/test-utils";
describe("App", () => {
test("renders without error", () => {
const wrapper = mount(<App />);
expect(wrapper.find(".App").exists()).toBe(true);
});
test("renders learn react link", () => {
const wrapper = mount(<App />);
act(() => {
new Promise((resolve) => setTimeout(resolve, 0));
});
wrapper.update();
console.log(wrapper.debug());
expect(wrapper.find(".App-link").text()).toBe("Learn React");
});
});
CodePudding user response:
Using enzyme for react testing ?
Enzyme is an old testing library for React.
True support support is stoped at React v16.
Wojciech Maj (creator of enzyme-adapter-react-17
that you are using explain why we should stop using it https://dev.to/wojtekmaj/enzyme-is-dead-now-what-ekl)
How to do async testing with enzyme
With enzyme only
Using setImmediate
import React from "react";
import { mount } from "enzyme";
import App from "./App";
describe("App", () => {
test("renders learn react link", (done) => {
const wrapper = mount(<App />);
setImmediate(() => {
component.update()
return expect(wrapper.find(".App-link").text()).toBe("Learn React");
done()
});
});
});
Mixing it with react-testing library
React testing library have a util call waitFor
that allow you to wait until expect is match
import React from "react";
import { mount } from "enzyme";
import { waitFor } from '@testing-library/react';
import App from "./App";
describe("App", () => {
test("renders learn react link", async () => {
const wrapper = mount(<App />);
await waitFor(() => {
wrapper.update()
return expect(wrapper.find(".App-link").text()).toBe("Learn React");
})
});
});
How to do async testing with react-testing-library
If you want to migrate to react testing library, your test will looks like this:
import React from "react";
import { waitFor, render } from '@testing-library/react';
import App from "./App";
describe("App", () => {
test("renders learn react link", async () => {
const { getByText } = render(<App />);
await waitFor(() => expect(getByText("Learn React")).toBeInTheDocument())
});
});
The main difference here is that you have queries to find your elements