고차 컴포넌트로 React Native Jest 테스트 설정하기

React Native 앱이 우리 앱과 같은 것이면 래퍼 위에 래퍼가 있고 화면과 화면 구성 요소를 래핑합니다. 몇 가지 예는 다음과 같습니다.

  • SafeAreaProvider - 장치의 안전 영역에만 액세스하도록 하기 위해

  • ThemeProvider - Styled Components와 같은 것을 사용하여 전체 앱에 테마 컨텍스트를 제공한다고 가정합니다.

  • Redux - 앱 전체에서 상태를 관리하기 위해

  • 구성 요소가 하나 이상의 고차 구성 요소(HoC)에서 제공하는 것에 부주의하게 의존할 수 있기 때문에 이는 단위 및 통합 테스트를 작성할 때 일이 까다로울 수 있습니다.

    Jest 테스트 설정을 단순화하기 위해 테스트별로 필요한 HoC를 쉽게 활용할 수 있도록 몇 가지 도우미 함수를 작성했습니다. 일을 더 단순하게 만드는 것은 개발 시간을 단축하면서 더 많은 테스트를 작성하는 장벽을 낮추는 것을 의미하므로 이것이 큰 승리입니다. 🎉

    다음은 Typescript에서 수행할 수 있는 방법의 예입니다. 우리가 사용하는 외부 패키지는 Redux Toolkit, Styled Components 및 React Native Safe Area Context입니다.

    // testHelpers.tsx
    
    import * as React from 'react'
    import { getDefaultMiddleware } from '@reduxjs/toolkit'
    import lodash from 'lodash'
    import { SafeAreaProvider } from 'react-native-safe-area-context'
    import { Provider as ReduxProvider } from 'react-redux'
    import renderer, { ReactTestInstance } from 'react-test-renderer'
    import createMockStore from 'redux-mock-store'
    import { ThemeProvider } from 'styled-components/native'
    
    import { TRootState } from '@app/core/state/root'
    import { initialState } from '@app/core/state/mockedInitialState'
    import { theme } from '@app/themes'
    
    type DeepPartial<T> = {
      [P in keyof T]?: DeepPartial<T[P]>
    }
    
    type TConfig = {
      mockRedux?: boolean
      mockSafeAreaProvider?: boolean
      mockTheme?: boolean
      state?: DeepPartial<TRootState>
    }
    
    const initialMetrics = {
      frame: { height: 0, width: 0, x: 0, y: 0 },
      insets: { bottom: 0, left: 0, right: 0, top: 0 },
    }
    
    export function createMockedElement(element: React.ReactElement, config?: TConfig) {
      let mockedElement = element
    
      if (config?.mockRedux !== false) {
        const middlewares = getDefaultMiddleware()
        const mockStore = createMockStore(middlewares)
        const state = lodash.merge(initialState, config?.state)
        const store = mockStore(state)
        mockedElement = <ReduxProvider store={store}>{mockedElement}</ReduxProvider>
      }
    
      if (config?.mockTheme !== false) {
        mockedElement = <ThemeProvider theme={theme}>{mockedElement}</ThemeProvider>
      }
    
      if (config?.mockSafeAreaProvider !== false) {
        mockedElement = <SafeAreaProvider initialMetrics={initialMetrics}>{mockedElement}</SafeAreaProvider>
      }
    
      return mockedElement
    }
    
    export function createReactTestInstance(element: React.ReactElement, config?: TConfig): ReactTestInstance {
      return renderer.create(createMockedElement(element, config)).root
    }
    


    여기에서 진행 중인 일이 매우 많으니 분해해 보겠습니다. 하지만 먼저 이야기해야 할 것은...

    도우미 함수가 실제로 어떻게 사용되는지



    저는 항상 이러한 도우미 메서드를 야생에서 사용하는 방법을 먼저 이해하는 것이 더 쉽다고 생각합니다. 따라서 이러한 도우미를 테스트에 통합하는 방법에 대한 예를 추가했습니다. 이것은 예상 요소의 존재 여부를 확인하는 데 유용한 React’s Test Renderer을 사용합니다.

    import { createReactTestInstance } from './testHelpers'
    
    describe('MyComponent tests', () => {
      it('renders correct version for users who shown interest', () => {
        const instance = createReactTestInstance(<MyComponent />)
    
        expect(instance.findByProps({ testID: `interested-icon` })).toBeTruthy()
      })
    
      it('renders correct version for users who have not shown interest', () => {
        const instance = createReactTestInstance(<MyComponent />)
    
        expect(instance.findByProps({ testID: `not-interested-icon` })).toBeTruthy()
      })
    })
    


    특정 사용자 작업이 특정 기대치를 초래하는지 여부를 테스트하려는 경우 React Testing Library(React의 테스트 렌더러 위에 위치)가 적합합니다. createReactTestInstance 도우미를 사용하는 대신 createMockedElement 도우미를 사용할 수 있습니다. 다음은 예입니다.

    import { fireEvent, render } from '@testing-library/react-native'
    import { act } from 'react-test-renderer'
    
    import { createMockedElement } from './testHelpers'
    
    const navigateMock = jest
      .mock
      // your mock...
      ()
    
    describe('BackButton tests', () => {
      it('navigates to the right screen onPress', async () => {
        const mockedElement = createMockedElement(<BackButton previousScreen="PreviousScreenName" />)
    
        const renderAPI = await render(mockedElement)
    
        await act(async () => {
          const backButton = renderAPI.getByTestId('button-back-navigation')
          await fireEvent.press(backButton)
          expect(navigateMock).toHaveBeenCalledWith('PreviousScreenName')
        })
      })
    })
    


    이제 도우미 함수가 실제로 어떻게 사용되는지 이해했으므로 도우미 파일을 설정하는 방법으로 돌아가 보겠습니다.

    도우미 파일 방식 깨기



    이 파일의 핵심은 createMockedElement 함수입니다.

    export function createMockedElement(element: React.ReactElement, config?: TConfig) {
      let mockedElement = element
    
      if (config?.mockRedux !== false) {
        const middlewares = getDefaultMiddleware()
        const mockStore = createMockStore(middlewares)
        const state = lodash.merge(initialState, config?.state)
        const store = mockStore(state)
        mockedElement = <ReduxProvider store={store}>{mockedElement}</ReduxProvider>
      }
    
      if (config?.mockTheme !== false) {
        mockedElement = <ThemeProvider theme={theme}>{mockedElement}</ThemeProvider>
      }
    
      if (config?.mockSafeAreaProvider !== false) {
        mockedElement = <SafeAreaProvider initialMetrics={initialMetrics}>{mockedElement}</SafeAreaProvider>
      }
    
      return mockedElement
    }
    


    이 함수는 테스트하려는 요소/구성 요소와 선택적config 개체의 두 가지 인수를 사용합니다. 이 구성 개체를 사용하면 테스트 중에 구성 요소를 렌더링할 때 포함할 래퍼(있는 경우)를 지정할 수 있습니다. 예를 들어 Redux 상태를 조롱해야 하는 경우 다음과 같이 테스트를 설정할 수 있습니다.

    it("doesn't open the modal when row is active", async () => {
      const mockedState = { show_modal: false }
      const config = { state: mockedState }
    
      const mockedElement = createMockedElement(<Row />, config)
    
      const renderAPI = await render(mockedElement)
    
      await act(async () => {
        // ... your test expectations
      })
    })
    

    ThemeProvider 및/또는 SafeAreaProvider 래퍼를 포함해야 하는 경우에도 마찬가지로 동일하게 수행할 수 있습니다. TConfig 에 정의된 대로 이 두 옵션은 boolean 입력을 받습니다.

    Redux 상태 설정에 대해 자세히 알아보기



    Redux 상태를 조롱할 때 테스트 Redux 상태가 일부 초기 값으로 설정되었는지 확인해야 합니다. 이를 위해 다양한 Redux Toolkit 슬라이스에서 모든 초기 상태를 추출하고 단일 객체로 결합한 다음 lodash 병합 기능에 전달했습니다(모의 상태와 완전히 병합되도록).

    // @app/core/state/mockedInitialState
    
    import { initialStateFeature1 } from '@covid/core/state/feature1.slice'
    import { initialStateFeature2 } from '@covid/core/state/feature2.slice'
    import { initialStateFeature3 } from '@covid/core/state/feature3.slice'
    
    export const initialState: TRootState = {
      feature1: initialStateFeature1,
      feature2: initialStateFeature2,
      feature3: initialStateFeature3,
    }
    


    그리고 그게 다야! 이것이 React Native 테스트 생활을 조금 더 쉽게 만들어주기를 바랍니다. 😄 저를 위한 제안이나 개선 사항이 있으면 알려주세요. 저는 항상 테스트 게임을 즐기고 있습니다! 나는 https://bionicjulia.com에 있고 .

    좋은 웹페이지 즐겨찾기