---
# System prepended metadata

title: 'Testing a hook with a state update - Fri 2nd Oct, 2020'
tags: [C+, accordion, hooks, filters, hookstesting, programmingjournal, testing, mocks, '2020']

---

# Testing a hook with a state update - Fri 2nd Oct, 2020

I wanted to write some tests for my `useFilters` hook. This hook handles persisting selected filters to storage, fetchign them from storage, creating the query string for the feed query, and managing the filter Formik state.

The main thing I wanted to test was the returned `filterParams` string, as this needs to be correct.

First thing was to mock the storage library set and get methods:

```typescript=
jest.mock('my-storage-lib', () => ({
    get: jest.fn(),
    set: jest.fn().mockResolvedValue('Successfully saved'),
}))
```

The next challenging part was that my hook uses a `useEffect` hook to fetch the persisted filters from storage and set back into Formik state. Since this state update is not "controlled" (ie. controlled by a prop like in [the hooks testing library example](https://github.com/testing-library/react-hooks-testing-library#usecountertestjs)), I would both get the dreaded `act` warning, and the `filterParams` value wasn't correct in the test.

After some trial and error, this was the solution:

```typescript=
jest.spyOn(myStorageLib, 'get').mockResolvedValue(JSON.stringify(mockInitialValues))
const { result, rerender } = renderHook(() => useFilters(), { wrapper: Wrapper })

await act(async () => {
    rerender(true)
})
expect(result.current.filterParams).toBe(expected)
```

You need to wait for the hook to `renrender` asynchronously before calling your expect.

```typescript=
test.each`
    case | myFilter    | groups     | customFilters     | expected
    ${1} | ${null}     | ${[]}      | ${[]}             | ${expected1}
    ${2} | ${myFilter} | ${[group]} | ${[customFilter]} | ${expected2}
    ${3} | ${myFilter} | ${[]}      | ${[]}             | ${expected3}
    ${4} | ${null}     | ${[group]} | ${[customFilter]} | ${expected4}
`('Case $case: it should return $expected', async ({ myFilter, groups, customFilters, expected }) => {
    jest.spyOn(RNSecureStorage, 'get').mockResolvedValue(JSON.stringify(mockInitialValues))
    const { result, rerender } = renderHook(() => useFilters(), { wrapper: Wrapper })

    await act(async () => {
        rerender(true)
    })
    expect(result.current.filterParams).toBe(expected)
})
```

I also used the decent [react-native-collapsible](https://github.com/oblador/react-native-collapsible) instead of building my own accordion component for the filters.


###### tags: `programmingjournal` `2020` `C+` `filters` `hooks` `testing` `hookstesting` `mocks` `accordion`