Step 1: mock modules
- Understanding Jest Mocks:
fn
,mock
andspyOn
- Mocking React hooks
After these two readings I am able to mock modules that returns a dynamic value (with jest’s mock naming convention).
Step 2: testing useEffectReducer
To test hooks, there’s @testing-library/react-hooks
Challenge 1: state is not updating after dispatch
Note that state got from [state, dispatch] = result.current
will not get updated:
The current value of the result will reflect the latest of whatever is returned from the callback passed to renderHook.
In order to see what happens after dispatch
, a new current
needs to be obtained from result
- or use result.current[0]
Challenge 2: Warning - “An update to TestComponent inside a test was not wrapped in act(…).”
After investigation, I found that comes from code blocks like this:
1 | someAsyncMethod() |
I wrapped the dispatch
in the test script with act
but cannot wrap this one.
I did not see this problem in the tests of useReducer
because useReducer
does not have effects that make function calls to do the async loads - useReducer
depends on dispatch
to move forward to the next state, while useEffectReducer
depends on the effects.
Therefore instead of calling dispatch
in my test script like in this Stackoverflow discussion on how to test useReducer, I should mock someAsyncMethod
‘s resolved value.
Challenge 3: Cannot mock readonly imported property someAsyncMethod
Solved with this reading: How To Spy On An Exported Function In Jest
Challenge 4: Mock error tracking function
1 | const mockTrackError = jest.fn() |
This is getting:
1 | ReferenceError: Cannot access 'mockTrackError' before initialization |
Fixed block:
1 | const mockTrackError = jest.fn() |
More details see: FIX JEST MOCK ‘CANNOT ACCESS BEFORE INITIALIZATION’ ERROR
Challenge 4: Mock method seems to be using the implementation from the prior test suite
Two points to be aware of:
jest.clearAllMocks
: Clears the mock.calls and mock.instances properties of all mocks. Equivalent to calling .mockClear() on every mocked function. It does NOT clear the implementation mock!- If you do
mockImplementationOnce
inbeforeEach
, you kind of need to make sure it really gets called once in each test. The “leftover” implementation will be carried out to the next test suite - which was why I got the bizarre behaviour that the test outcome changed when I swapped two test suites.