e2e 테스트코드 작성을 위해 detox 라이브러리를 도입하다

박재현 ( Jcurver )·2023년 1월 24일
0

왜 앱 런칭단계에서 테스트를 도입하게 되었나?

기존에는 팀원분들께서 감사하게도 함께 화면의 컴포넌트를 일일이 눌러주시면서 테스트를 진행했습니다. 이렇게 진행한데에는 다음과 같은 이유가 있습니다.

  1. MVP를 만들어내는 기간동안은 작업 분량을 소화하느라 test를 고려할 여유가 없었다.
  2. 초기에는 로직이 다양하지 않고 navigation이 일어나는 stream이 단순해서 test할 필요성이 높지 않았다.(side effect 발생 빈도가 높지 않았다)

시간이 지나면서 아래와 같은 현상이 발생했습니다.

  1. 점점 코드의 양이 비대해지고, MVP가 완성되어가면서 페이지 작업보다는 크고 작은 이슈들이 개발에 병목현상을 일으키는 주 원인으로 바뀌었다.
  2. App Store에서 앱을 심사받는 과정에서 실제로 치명적인 로그인 이슈가 터져서 재심사를 요청해야 했다. 심사로 인해 2일 이상 delay가 발생하게 되었고 이는 너무나도 치명적이다.

이에 따라 팀원들이 직접 유저 테스트를 진행하는것이 전보다 더 비효율적이게 되었습니다.
개발비용 측면에서 ROI를 짐작해봤을 때, test를 진행해야 할 분기점이 되었다고 판단했습니다.

테스팅 기법이 여러가지가 있는데 그 중 왜 e2e테스트를 도입하게 되었나?

테스팅 기법부터 정리해보면, 프론트엔드에서 진행할 수 있는 testing 기법은 아래 3개로 정리할 수 있습니다.

Unit Test

작성한 애플리케이션에서 테스트 가능한 가장 작은 단위의 코드를 테스트하는 기법이다. 여러 작은 단위의 테스트들이 독립적으로 참, 거짓을 판단하기 때문에 테스트가 실패했을 경우, 어느 부분이 문제인지 빠르게 파악할 수 있지만 애플리케이션의 전체적인 플로우가 정상임을 보장하지는 않는다.

Integration Test

애플리케이션에서 두 가지 이상의 요소가 함께 상호 작용할 때, 개발자가 의도한 대로 동작하는지 테스트하는 기법이다. 프론트엔드 개발 환경에서 봤을 때, Store에 연결(connect)된 Component를 테스트하는 경우를 예로 들 수 있을 것 같다.

E2E Test(End to End Test)

기능 테스트(Funtional Test)라고도 불리는 이 테스팅 기법은 말 그대로 끝에서 끝까지 테스트하는 기법이다. 사용자가 직접 애플리케이션을 사용하는 것처럼 동작하도록 스크립트를 작성하고 이것을 실제 실행시켜보면서 기대한대로 동작하는지 검증할 수 있다.

Keepit 프로젝트에서 가장 자주 발생하고 찾기 어려웠던 이슈는 코드를 수정하며 발생하는 다양한 side Effect 관련 이슈였습니다.

예컨대, 리뷰 보는 화면에서 업로드한 리뷰가 제대로 노출되지 않아서 리뷰 posting하는 페이지를 수정했더니, 지도페이지에서 리뷰 posting 페이지로 접속 시 에러가 나기도 했습니다. 페이지 간 의존성 제거가 제일 좋은 해결방법이라고 생각하지만, 모든 페이지의 의존성을 없애기는 어렵기에 이런부분에서 테스트의 필요성을 느끼게 되었습니다.

따라서 애플리케이션을 직접 구동시켜서 화면이 잘 작동하는지 확인하는것이 가장 중요하고, 가장 직관적으로 접근할 수 있는 테스트방식은 e2e라고 판단했습니다. 테스트는 100% 커버리지를 만들기는 어렵고 효율도 나오지 않아서 특정 test기법을 선택하기로 했고, Unit test를 위해서는 미리 함수를 짤 때 순수 로직을 분리해 놓는 편이 좋은데 이를 고려하고 MVP를 제작하지는 못한 점도 하나의 이유로 작용했습니다.

그렇다면 detox 라이브러리를 선택한 이유는 무엇인가?

React-native에서 e2e를 할 수 있는 선택지는 그리 많지 않았습니다. 그래서 React-Native공식문서에서 가장 메인으로 추천하는 detox라이브러리를 확인해보았습니다. 확인해본 결과 Jest를 한번 감싸고 있는 라이브러리이고 10K 이상의 star가 있고 공식 홈페이지의 docs가 잘 구성되어있어서 설치해보았습니다.

사용해본 결과, docs대로 진행하니 생각보다 어렵지 않게 구현할 수 있었습니다. Jest와 문법도 거의 동일했습니다.
앱을 런칭하는 과정에서 로그인 에러로 인해서 App Store에서 Reject를 당한 적이 있어서 먼저 로그인 로직 테스트를 작성해보았습니다.

Login Test Code

describe('login logic', () => {
  beforeAll(async () => {
    await device.launchApp();
  });

  beforeEach(async () => {
    await device.reloadReactNative();
  });

  it('login stream', async () => {
    await expect(element(by.text('우리들만의 장소를 모으다'))).toBeVisible();
    await element(by.text('시작하기')).tap();
    await expect(element(by.text('휴대폰 번호로 가입'))).toBeVisible();
    await expect(element(by.text('로그인하기'))).toBeVisible();
    await element(by.id('dologin')).tap();
    await expect(element(by.text('휴대폰 번호로 로그인'))).toBeVisible();
    await element(by.id('login-input')).typeText('01029511523');
    await element(by.text('문자 인증번호 받기')).tap();
    await expect(element(by.text('문자 인증번호 입력'))).toBeVisible();
    await element(by.id('BottomSheetTextInput')).typeText('000000');
    await element(by.text('확인')).tap();
    await expect(element(by.text('자주 찾는 그룹'))).toBeVisible();
  });
});

모든 함수들이 pipe로 연결되어있어서 깔끔하게 한줄로 처리되고, by.idby.text를 통해 어떤 로직이 진행되었는지 쉽게 알아볼 수 있었습니다.

Trouble Shooting

코드 내부에 3분안에 인증번호를 입력하도록 하는 로직이 있습니다. 화면에서 1초마다 숫자가 줄어들기에 1초마다 컴포넌트 리렌더링이 발생합니다. 이를 위해 기존에는 setTimeout과 Timer로 interval을 통해 구현해 두었습니다.

하지만 detox에서 계속 Task Queue를 확인하는데, Task가 항상 남아있어서 테스트 로직이 진행되지 못하는 현상이 발생했습니다. detox Docs에서 관련된 내용을 찾아보니 아래와 같은 내용이 있었습니다.

By default, Detox is designed to ignore setInterval and will only wait for setTimeout of up to 1.5 seconds. If you have an endless polling loop with short intervals implemented with setTimeout, switch the implementation to setInterval. If possible, avoid aggressive polling in your app altogether, the poor single JavaScript thread we have doesn’t like it.

setInterval은 테스팅에서 무시되며 setTimeout은 1.5초만 waiting한다는 내용이었습니다. 해당 setTimeout 코드는 setInterval로 리팩토링하는 쪽이 바람직하다고 판단하여, setTimeout과 Timer를 customHook인 useInterval로 교체하였습니다. 그 결과 interval이 무시되어서 테스팅이 계속 진행되어 통과되었습니다.

테스트에서 버튼을 누르고 발생하는 event들의 해결 여부를 알기 위해서 detox가 Task Queue를 바라보고있고, Task Queue가 비어있는지 여부를 체크하고 있다는 점이 인상적이었습니다. 또한 어떤 tast queue가 남아있는지 모두 보여주니 디버깅에 용이하다고 느꼈습니다.

앞으로도 detox 테스팅 라이브러리로 테스트 커버리지를 높여가야겠습니다. 그리고 어떤 경로로 탐색했을 때 더 의미있는 테스트가 될 것인지 고민해보겠습니다.

profile
FE developer / Courage is very important when it comes to anything.

0개의 댓글