Jest.js react测试库,如何测试useEffect中的setState

tpgth1q7  于 2022-12-08  发布在  Jest
关注(0)|答案(1)|浏览(279)

I have written a component that does two things :

  1. show a banner: when netInfo.isConnected change and is false or when netInfo.isInternetReachable change and is still false after 3 seconds
  2. hide a banner: when netInfo.isConnected or netInfo.isInternetReachable change to true
    For (1) hiding after 3 seconds is possible by creating a ref that can be readen 3 seconds later.
    (2) is pretty straightforward, a simple useEffect can help me to do that, my components looks like this:
export function OfflineModeContainer({ children }: { children: ReactNode }) {
  const netInfo = useNetInfo()
  const [show, setShow] = useState(!netInfo.isConnected)
  const isInternetReachable = useRef(netInfo.isInternetReachable)

  useEffect(() => {
    setShow(!netInfo.isConnected)
  }, [netInfo.isConnected])

  useEffect(() => {
    isInternetReachable.current = netInfo.isInternetReachable
    let timer: number | undefined
    if (!isInternetReachable.current) {
      timer = setTimeout(() => {
        if (!isInternetReachable.current) {
          setShow(true)
        }
      }, 3000)
    } else {
      setShow(false)
    }
    return () => {
      if (timer) {
        clearInterval(timer)
      }
    }
  }, [netInfo.isInternetReachable])

  return (
    <View>
      <View>{children}</View>
      {show ? (
        <View>
          <View>{t`aucune connexion internet.`}</<View>
        </<View>
      ) : null}
    </View>
  )
}

This works fine. I now want to test both useEffect using React testing library.

  • How can I test the 1st useEffect and 2nd useEffect for the hide feature when one of the value switch to true ? I believe the difficulty here is to trigger the hook after a while during the test
  • How can I test the setTimeout asynchronous operation?
  • How to test the clearTimer

Live demo reproduction

https://codesandbox.io/s/react-native-test-forked-7nc5zc
Unfortunately, I can't use mock in codesandbox due to known open issue with jest.mock , still open until 2018 https://github.com/codesandbox/codesandbox-client/issues/513
For this reason, I add the testing logs here:

$ TZ=UTC JEST=true jest --forceExit /home/dka/workspace/github.com/pass-culture/pass-culture-app-native/src/libs/network/__tests__/OfflineModeContainer.test.tsx
 FAIL  src/libs/network/__tests__/OfflineModeContainer.test.tsx (5.178 s)
  <OfflineModeContainer />
    ✓ should render children and show banner when offline at init when isConnected is false (36 ms)
    ✓ should render children and not show banner when offline at init when isConnected is true (5 ms)
    ✕ should not show "aucune connexion internet." at init, then show when isConnected is false, then hide when isConnected switch back to true (558 ms)

  ● <OfflineModeContainer /> › should not show "aucune connexion internet." at init, then show when isConnected is false, then hide when isConnected switch back to true

    expect(received).toBeFalsy()

    Received: {"_fiber": {"_debugHookTypes": null, "_debugID": 163, "_debugNeedsRemount": false, "_debugOwner": [FiberNode], "_debugSource": null, "actualDuration": 0, "actualStartTime": -1, "alternate": null, "child": [FiberNode], "childLanes": 0, "dependencies": null, "elementType": [Function Component], "firstEffect": null, "flags": 1, "index": 0, "key": null, "lanes": 0, "lastEffect": null, "memoizedProps": [Object], "memoizedState": null, "mode": 0, "nextEffect": null, "pendingProps": [Object], "ref": null, "return": [FiberNode], "selfBaseDuration": 0, "sibling": null, "stateNode": [Component], "tag": 1, "treeBaseDuration": 0, "type": [Function Component], "updateQueue": [Object]}}

      50 |       </OfflineModeContainer>
      51 |     )
    > 52 |     expect(await renderAPI.queryByText('aucune connexion internet.')).toBeFalsy()
         |                                                                       ^
      53 |   })
      54 | })
      55 |
b1zrtrql

b1zrtrql1#

我是这样解决这个问题的:

import React from 'react'
import { View, Text } from 'react-native'

import { useNetInfoContext as useNetInfoContextDefault } from 'libs/network/NetInfoWrapper'
import { OfflineModeContainer } from 'libs/network/OfflineModeContainer'
import { render } from '@testing-library/react-native'

jest.mock('libs/network/useNetInfo', () => jest.requireMock('@react-native-community/netinfo'))

const mockUseNetInfoContext = useNetInfoContextDefault as jest.Mock

describe('<OfflineModeContainer />', () => {
  mockUseNetInfoContext.mockImplementation(() => ({ isConnected: false }))
  it('should not show "aucune connexion internet." at init, then show when isConnected is false, then hide when isConnected switch back to true', () => {
    mockUseNetInfoContext.mockImplementationOnce(() => ({
      isConnected: true,
      isInternetReachable: true,
    }))
    const renderAPI = renderOfflineModeContainer()
    expect(renderAPI.queryByText('aucune connexion internet.')).toBeFalsy()

    mockUseNetInfoContext.mockImplementationOnce(() => ({
      isConnected: false,
      isInternetReachable: false,
    }))
    renderAPI.rerender(getJsx())

    expect(renderAPI.queryByText('aucune connexion internet.')).toBeTruthy()
  })
})

// for rerender, it cannot be static, it has to be new
const getJsx = () => (
  <OfflineModeContainer>
    <View>
      <Text>Hello World</Text>
    </View>
  </OfflineModeContainer>
)

function renderOfflineModeContainer() {
  return render(getJsx())
}

相关问题