본문 바로가기
source-code/FrontEnd

testing-library/react를 통한 컴포넌트 테스트 (with RN + expo)

by mattew4483 2023. 8. 18.
728x90
반응형

2023.08.18 - [source-code/FrontEnd] - Jest를 통한 unit test

 

Jest를 통한 unit test

테스트 코드. 초기 스타트업 개발자, 특히 프론트엔드 개발자들이 가장 놓치기 쉬운 요소가 아닐까. 왜? → 제품의 명확한 기능이나 디자인 시스템이 갖춰지지 않은 상태로 개발이 진행되는 경

23life.tistory.com

Jest를 통해 특정 Class내 method가 의도대로 동작하는지에 대한 단위 테스트를 작성했었다.
 
하지만 (특히) 프론트엔드 개발자가 테스트하고 싶은 항목은

  • 특정 상황에서 의도한 화면이 잘 뜨는지
  • 사용자 액션 발생 (클릭, 타이핑, 스크롤 등) 시 의도대로 동작하는지
  • 비동기 작업 발생 시 화면의 의도대로 표시되는지

와 같은, UI적 요소가 대부분일 테다!
 
https://testing-library.com/docs/react-testing-library/intro/

 

React Testing Library | Testing Library

React Testing Library builds on top of DOM Testing Library by adding

testing-library.com

이전에 사용했던 Jest와 React Testing Library를 사용,
DOM을 기준으로 사용자의 행위 기반 테스트를 작성해 볼 예정!
 

설치

현재 서비스는 React Native + Expo 환경으로 구성되어 있다.
https://docs.expo.dev/develop/unit-testing/

 

Unit testing

Learn how to set up and configure the jest-expo package to write unit tests and snapshot tests for a project.

docs.expo.dev

expo 공식 문서 내 unit testing 항목을 참고하며 환경세팅을 진행했다.

yarn add --dev jest-expo jest

 
다음은 testing-library/react를 설치할 차례인데...
@testing-library/react 의 render 함수를 통해 컴포넌트를 렌더링 할 경우
React Native는 브라우저 환경에서 동작하는 것이 아니기 때문에, 해당 DOM을 올바르게 구성하지 못하는 에러가 발생한다.
 
https://github.com/callstack/react-native-testing-library

 

GitHub - callstack/react-native-testing-library: 🦉 Simple and complete React Native testing utilities that encourage good tes

🦉 Simple and complete React Native testing utilities that encourage good testing practices. - GitHub - callstack/react-native-testing-library: 🦉 Simple and complete React Native testing utilities t...

github.com

 

yarn add --dev @testing-library/react-native

따라서 @testing-library/react-native를 사용!
 

yarn add --dev react-dom react-test-renderer

react-dom과 react-test-renderer도 추가적으로 설치했다.
 

package.json 설정

// package.json
"scripts": {
  ...
  "test": "jest"
},
"jest": {
  "preset": "jest-expo",
  "transformIgnorePatterns": [
      "node_modules/(?!((jest-)?react-native|@react-native(-community)?)|expo(nent)?|@expo(nent)?/.*|@expo-google-fonts/.*|react-navigation|@react-navigation/.*|@unimodules/.*|unimodules|sentry-expo|native-base|react-native-svg)"
    ]
}

@testing-library/react-native를 사용할 경우,
preset을 "@testing-library/react-native"로 설정하는 게 일반적이다.
 
그러나 이 경우 expo 의존 모듈 호출 시 테스트가 실패하게 되는데,
현 프로젝트에는 이미 수많은 expo 라이브러리가 설치되어 있었다.
(expo-font, expo-secure-store 등등등...)
 
Jest의 테스트 환경에서는 해당 의존성 모듈에 대한 정의가 존재하지 않으므로

jest.mock("expo-font");
jest.mock("expo-secure-store");
...

엄청난 양의 라이브러리 함수들을 직접 mocking 해줘야만 했다.
→ preset을 jest-expo로 설정할 경우, 해당 모듈이 포함된 테스트도 정상적으로 동작할 수 있게 된다.
 

trouble shooting

Provider

redux Provider를 사용 중이라면, render를 통해 컴포넌트 호출시 에러가 발생한다.
(useSelector는 provider 내부에 존재해야 한다는 에러)
 
https://redux.js.org/usage/writing-tests

 

Writing Tests | Redux

Usage > Writing Tests: recommended practices and setup for testing Redux apps

redux.js.org

친절한 공식문서 → renderWithProviders를 작성해, 앱 내에서 사용 중인 state를 넘겨 감싸주면 간단히 해결된다.
 
이와 유사한 에러가
react-query, react-navigation 등을 사용해 발생했고,
역시 유사한 해결 방법(render 함수 내 wrapper 옵션에 필요한 컴포넌트들을 wrapping)으로 해결할 수 있었다.

export function renderWithAllWrappers(
  ui,
  {
    preloadedState = {},
    // Automatically create a store instance if no store was passed in
    store = setupStore(preloadedState),
    ...renderOptions
  } = {}
) {
  function Wrapper({ children }) {
    return (
      <NavigationContainer>
        <QueryClientProvider client={queryClient}>
          <Provider store={store}>{children}</Provider>
        </QueryClientProvider>
      </NavigationContainer>
    );
  }
  return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
}

 

expo-font

테스트 코드를 통해 의도한 text가 정상적으로 DOM에 그려지는지를 확인했다.

import { render } from "@testing-library/react-native";

describe("<Test />", () => {
  it("renders correctly", () => {
    const tree = render(<Test />);

    const elementWithText = tree.getByText("테스트");
    expect(elementWithText).toBeTruthy();
  });
});

다음과 같이 작성했지만... 실패한 테스트.
 
분명 해당 텍스트가 화면에 존재하는데

온데간데 사라진 Text

테스트 환경 DOM에는 Text 컴포넌트가 정말로 존재하지 않았다.
 

import { Text } from "react-native";
import { useFonts } from "expo-font";
  
const Text = ({children}) => {
  const [loaded] =  useFonts({
    Font: require("./Font.otf"),
  });

  if (!loaded) {
    return null;
  }
  
  return <Text>{children}</Text>
}

원인은? 앱 내 font 적용을 위해 useFonts를 통해 폰트를 적용한 Text 컴포넌트를 사용하고 있었는데...
해당 hooks가 비동기로 동작하면서, loaded가 false인 상태로 테스트가 시작-종료돼버린 것.
 

jest.mock("expo-font", () => ({
   useFonts: jest.fn().mockReturnValue([true]), // 반환값에 loaded: true를 포함
}));

해당 hooks를 mocking 해, 항상 true를 반환할 수 있도록 수정했다.
(해당 폰트가 잘 불러와졌는지는 테스트의 관심사가 아니라 판단)
 

styled-component

styled-component로 작성된 스타일 컴포넌트 모든 곳에서 에러가 발생했다!

TypeError: Cannot read properties of undefined (reading 'withConfig') 에러가 발생했는데...

 

import styled from "styled-components";
...

const StyledView = styled.View`
 ... 
`

위와 같이 styled를 사용해 발생한 문제였다.

 

import styled from "styled-components";
import { View } from 'react-native'
...

const StyledView = styled(View)`
 ... 
`

위와 같이 직접 react-native의 View 컴포넌트를 직접 상속하거나

 

import styled from "styled-components/native";
...

const StyledView = styled.View`
 ... 
`

styled-components/native에서 styled를 import 해 해결할 수 있었다.

728x90
반응형