我已经创建了用于测试异步钩子的示例。
https://github.com/oshri6688/react-async-hooks-testing
CommentWithHooks.js
:
import { getData } from "services/dataService";
const CommentWithHooks = () => {
const [data, setData] = useState(null);
const [isLoading, setIsLoading] = useState(true);
const fetchData = () => {
setIsLoading(true);
getData()
.then(data => {
setData(data);
})
.catch(err => {
setData("No Data");
})
.finally(() => {
setIsLoading(false);
});
};
useEffect(() => {
fetchData();
}, []);
return (
<div>
{isLoading ? (
<span data-test-id="loading">Loading...</span>
) : (
<span data-test-id="data">{data}</span>
)}
<button
style={{ marginLeft: "20px" }}
data-test-id="btn-refetch"
onClick={fetchData}
>
refetch data
</button>
</div>
);
};
CommentWithHooks.test.js
:
import React from "react";
import { mount } from "enzyme";
import { act } from "react-dom/test-utils";
import MockPromise from "testUtils/MockPromise";
import CommentWithHooks from "./CommentWithHooks";
import { getData } from "services/dataService";
jest.mock("services/dataService", () => ({
getData: jest.fn(),
}));
let getDataPromise;
getData.mockImplementation(() => {
getDataPromise = new MockPromise();
return getDataPromise;
});
describe("CommentWithHooks", () => {
beforeEach(() => {
jest.clearAllMocks();
});
it("when fetching data successed", async () => {
const wrapper = mount(<CommentWithHooks />);
const button = wrapper.find('[data-test-id="btn-refetch"]');
let loadingNode = wrapper.find('[data-test-id="loading"]');
let dataNode = wrapper.find('[data-test-id="data"]');
const data = "test Data";
expect(loadingNode).toHaveLength(1);
expect(loadingNode.text()).toBe("Loading...");
expect(dataNode).toHaveLength(0);
expect(button).toHaveLength(1);
expect(button.prop("onClick")).toBeInstanceOf(Function);
await getDataPromise.resolve(data);
wrapper.update();
loadingNode = wrapper.find('[data-test-id="loading"]');
dataNode = wrapper.find('[data-test-id="data"]');
expect(loadingNode).toHaveLength(0);
expect(dataNode).toHaveLength(1);
expect(dataNode.text()).toBe(data);
});
testUtils/MockPromise.js
:
import { act } from "react-dom/test-utils";
const createMockCallback = callback => (...args) => {
let result;
if (!callback) {
return;
}
act(() => {
result = callback(...args);
});
return result;
};
export default class MockPromise {
constructor() {
this.promise = new Promise((resolve, reject) => {
this.promiseResolve = resolve;
this.promiseReject = reject;
});
}
resolve(...args) {
this.promiseResolve(...args);
return this;
}
reject(...args) {
this.promiseReject(...args);
return this;
}
then(...callbacks) {
const mockCallbacks = callbacks.map(callback =>
createMockCallback(callback)
);
this.promise = this.promise.then(...mockCallbacks);
return this;
}
catch(callback) {
const mockCallback = createMockCallback(callback);
this.promise = this.promise.catch(mockCallback);
return this;
}
finally(callback) {
const mockCallback = createMockCallback(callback);
this.promise = this.promise.finally(mockCallback);
return this;
}
}