Step 1: mock modules

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
2
3
4
5
6
7
8
9
10
someAsyncMethod()
.then(result => {
dispatch({ type: 'SOME_EVENT'})
})
.catch(error => {
dispatch({ 👈 warning produced here
type: 'ERROR',
errorMessage: SOME_MESSAGE,
})
})

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
2
3
4
const mockTrackError = jest.fn()
jest.mock('../errorTracking', () => ({
useTrackError: jest.fn().mockReturnValue(mockTrackError), 👈 `mockTrackError` will be evaluated upon `jest.mock`
}))

This is getting:

1
ReferenceError: Cannot access 'mockTrackError' before initialization

Fixed block:

1
2
3
4
5
const mockTrackError = jest.fn()
jest.mock('../errorTracking', () => ({
useTrackLSPAYError: jest.fn().mockImplementation(() => mockTrackError), 👈 `mockTrackError` will only be evaluated when test executes
TAG: { cardNotPresentPayment: 'card-not-present' },
}))

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 in beforeEach, 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.