-
-
Save mauricedb/eb2bae5592e3ddc64fa965cde4afe7bc to your computer and use it in GitHub Desktop.
import { useState } from 'react'; | |
export function useCounter(initial = 0) { | |
const [count, setCount] = useState(initial); | |
return [count, () => setCount(count + 1)]; | |
} |
import { useCounter } from './Calculator'; | |
const mockSetState = jest.fn(); | |
jest.mock('react', () => ({ | |
useState: initial => [initial, mockSetState] | |
})); | |
test('Can increment from 1 to 2', () => { | |
const [_, increment] = useCounter(1); | |
increment(); | |
expect(mockSetState).toHaveBeenCalledWith(2); | |
}); |
Thanks man!
Thanks a looooooooooooooooooooooot.
Nice, but we can't do this if we're testing React Components which uses useState
. Any suggestion about how to do that?
@marco-souza True but this trick is intended for testing hooks directly, not for testing components or hooks through a component. Use something like react-testing-library for that.
I see. So, I already solved that by using .spyOn
in useState
. Works like a charm 😸
FYI this helped me to achieve test a react function component using useState
:
import React, { useState as useStateMock } from "react";
jest.mock("react", () => ({
...jest.requireActual("react"),
useState: jest.fn()
}));
// And before rendering (omitting var declarations for brevity)
useStateMock.mockImplementation(initState => [initState, jest.fn()]);
component = shallow(<DetailPage {...mockProps} />);
This link helped me a bunch (go down to comments to see this solution): https://dev.to/theactualgivens/testing-react-hook-state-changes-2oga
The following technique works well for me testing functional components with useState
destructured:
import * as React from 'react';
describe('Some message', () => {
const setState = jest.fn();
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const useStateMock: any = (initState: any) => [initState, setState];
afterEach(() => {
jest.clearAllMocks();
});
it('Is a test where we want to mock useState', () => {
jest.spyOn(React, 'useState').mockImplementation(useStateMock);
const wrapper = shallow(<Component {...props} />);
// trigger setState somehow
expect(setState).toHaveBeenCalledTimes(1);
// Other tests here
});
});
This is typescript, but it should work just as well in JS if you remove the type annotations
@marco-souza how you solved using spyOn. You have any example? Thanks
@marco-souza how you solved using spyOn. You have any example? Thanks
import React, { useState as useStateMock } from 'react';
jest.mock('react', () => ({
...jest.requireActual('react'),
useState: jest.fn(),
}));
describe('testing useState mocked', () => {
const setState = jest.fn();
const useStateMock = (initState: any) => [initState, setState];
jest.spyOn(React, 'useState').mockImplementation(useStateMock);
afterEach(() => {
jest.clearAllMocks();
});
// your tests goes here
});
For me it's better having this logic in a custom hook and using this library for the tests instead https://github.com/testing-library/react-hooks-testing-library
how to spy specific useState
hook, when I have more than one useState
hook inside my component?
@muhsalaa, suppose you have a component like this:
const Component = () => {
const setState1 = useState(1);
const setState2 = useState(2);
...
};
In the test, you'll want to do:
import * as React from 'react';
...
it('Should work', () => {
const useStateSpy = jest.spyOn(React, 'useState')
...
expect(useStateSpy).toHaveBeenNthCalledWith(2, 2); // if testing the second `useState`
});
not working ...
How about
setState(prevState => ({ ...prevState, }))
?
How about
setState(prevState => ({ ...prevState, }))
?
Yeah What about class component How do I Change class setState ?
how mock many state in useState?
not working
jest.spyOn(React, 'useState')
.mockImplementationOnce(() => [false, () => null])
.mockImplementationOnce(() => [true, () => null])
You may consider using renderHook (testing-library/react). I find it easy to use.
Here is the sample code:
const { result } = renderHook(() => {
const [isOpen, setIsOpen] = useState(true)
return { isOpen, setIsOpen }
})
const renderValue = await act(() => {
return render(
<ProductFormDialog
isOpen={result.current.isOpen}
setIsOpen={result.current.setIsOpen}
/>
)
})
const displayValue = renderValue.getByTestId('header-container').textContent
expect(displayValue).toEqual('Header')
Thanks for sharing!