Reputation: 1657
I need to mock my custom hook when unit testing React component. I have read few tutorials and stackoverflow answers to this simple task, but without luck to implement it correctly.
My simplest possible setup for single test is as following:
// TestComponent.js
import React from "react";
import useTest from "./useTest";
const TestComponent = () => {
const { state } = useTest("initial_value");
return <div>{state}</div>;
};
export default TestComponent;
// useTest.jsx - simple custom hook
import React, { useState } from "react";
const useTest = (initialState) => {
const [state] = useState(initialState);
return { state };
};
export default useTest;
// TestComponent.test.jsx - my test case
import React from "react";
import { render } from "@testing-library/react";
import TestComponent from "./TestComponent";
jest.mock("./useTest", () => ({
useTest: () => "mocked_value",
}));
test("rendertest", () => {
const component = render(<TestComponent />);
expect(component.container).toHaveTextContent("mocked_value");
});
So I trying to mock useTest
custom hook to return "mocked_value", instead of "initial_value" from real custom hook. But above code just gives me this error:
TypeError: (0 , _useTest.default) is not a function
3 |
4 | const TestComponent = () => {
> 5 | const { state } = useTest("initial_value");
| ^
6 |
7 | return <div>{state}</div>;
8 | };
I have also tried:
import useTest from './useTest';
// ...
jest.spyOn(useTest, "useTest").mockImplementation(() => "mocked_value");
import useTest from './useTest';
// ...
jest.spyOn(useTest, "useTest").mockReturnValue("mocked_value");
But both gives me error Cannot spy the useTest property because it is not a function; undefined given instead
.
How do I implement this test?
Upvotes: 26
Views: 42717
Reputation: 97
For those who use the right hook it can be done this way
import * as useFetchMock from "../../../../hooks/useFetch";
it("should show loading component on button when state is loading", () => {
jest.spyOn(useFetchMock, "default").mockReturnValue({
result: null,
message: null,
status: "pending",
loading: true,
fetch: jest.fn(),
});
render(<Component />);
expect.assertions(1);
expect(screen.getByTestId(testIdComponentB)).toBeInTheDocument();
});
import your custom hook directly and use 'default' to interact directly
Upvotes: 4
Reputation: 1657
I'm answering to myself. This way it's working:
jest.mock("./useTest", () => ({
useTest: () => ({ state: 'mocked_value' }),
}));
And if I want to use default export in custom hook:
jest.mock("./useTest", () => ({
__esModule: true,
default: () => ({ state: 'mocked_value' }),
}));
Also, if I want to also use setState
method in my hook and export it, I can mock it like this:
const mockedSetState = jest.fn();
jest.mock("./useTest", () => ({
useTest: () => ({ state, setState: mockedSetState }),
}));
And now it's possible to check if setState
has been called once:
expect(mockedSetState).toHaveBeenCalledTimes(1);
Upvotes: 55
Reputation: 81
Since you use export default useTest
in useTest module, it expects to get that function reference from a default attribute in your mock.
Try this:
jest.mock("./useTest", () => ({
default: () => "mocked_value",
}));
If you want to avoid confusion, you could try export const useTest = ...
in useTest module and then import { useTest } from './useTest'
in your component. No need to change your test if using this approach.
Upvotes: 2