React Native Testing Library를 이용한 TDD 도전

휴먼스케이프

역할에 충실한 컴포넌트 만들기

안녕하세요, 휴먼스케이프 주니어 프론트엔드 개발자 Tasha입니다.

TDD를 도입해보기로 하게 된 이유

요즘은 휴먼스케이프에서 레어노트 코드의 상태관리 tool을 Conetxt API 기반에서 Redux로 변경하기로 결정하여 기존 코드들을 redux와 redux-saga로 이전하는 작업을 진행하였습니다. 상당부분의 코드들을 건드리게 되면서 기존 제가 작성한 코드와 작성하지 않은 코드들을 전반적으로 리팩토링을 하는 시간이 많았는데요. 이런 과정을 거치면서 몇 가지 문제점들을 발견할 수 있었습니다.

직접 작성하지 않은 코드의 기획을 정확히 파악하지 못해 한 곳을 리팩토링하면 다른 곳에서 버그가 나는 상황들을 자주 마주함

컴포넌트 설계가 불명확하여 로직을 이곳 저곳으로 옮기는 일들을 많이 함.

1번과 같은 이유로 테스트 도입에 대하여 고민해보았는데, 2번의 이유로 TDD에 대한 고민까지 같이 하게 되었습니다. 설계부분에서 크게 고민하지 않고 시간상의 이유로 급할 때 코드를 이곳 저곳에 붙이다보니 하나의 컴포넌트가 너무 다중적인 역할을 가지고 있는 경우나 책임과 역할 부분에서 불명확한 부분들이 있는 컴포넌트가 여럿 보였기 때문입니다.

객체지향적 설계 방법론 중 하나인 TDD를 차용하게 되면 컴포넌트를 만들 때 먼저 컴포넌트 역할에 대해 고민해보고, 그 역할을 중심적으로 설계하여 더 안정된 코드를 작성할 수 있겠다는 생각이 들었습니다. TDD도입을 당장에 고려해보기보다, 스스로의 개발 패턴에 변화를 주고 싶고 테스트 자체의 도입을 고려하기 위해 간단한 부분을 TDD로 개발해보기로 하였습니다.

TDD를 도입해 볼 부분

처음 시도해보는 거라 너무 복잡하지 않은 기능에 도입하고 싶었는데, 마침 필요한 기능이 있었습니다. 바로 QA용 빌드에 필요한 개발자메뉴인데요. QA를 진행하면 Dev/Prod 모드의 API를 각각 테스트 해 볼 필요가 있어 이를 직접 설정할 수 있도록 만든 것이 개발자메뉴입니다. 이 개발자메뉴는 휴먼스케이프의 다른 프로젝트인 미세톡톡에서 Henry가 직접 기획 & 개발을 훌륭하게 해주셔서 그 기획 및 UI 부분을 참고하기로 했습니다.

TDD를 하기 위한 컴포넌트 설계

개발자 메뉴를 각 책임/역할에 따라 네가지의 컴포넌트/모듈로 나눠보았습니다.

RadioButton

: 라디오버튼 형태의 컴포넌트입니다. 라디오버튼 특성에 따라 선택이 되어있지 않을때만 props로 받은 함수를 호출시키며 동그라미 영역이 검정색으로 채워집니다.

2. RadioButtonList

: 리스트를 props로 받아와서 RadioButton으로 렌더합니다.

3. DeveloperMenu

: RadioButtonList와 하단 버튼을 렌더하고 렌더하는 컴포넌트의 상태를 관리합니다. (AsyncStorage Switch 컴포넌트의 경우 이미 비워지는 기능이 다른 곳에 구현되어있어 이 부분에서는 생략하였습니다.)

4. Redux module

: apiMode 상태를 저장하고, 이를 변경하는 액션과 리듀서를 가지고 있습니다.

React-native Testing Library를 이용한 TDD

CRNA로 만든 App은 기본적으로 테스팅을 위한 jest가 설치되어있으며, 그 이외에 목적에 맞는 테스트를 위해 다른 라이브러리들을 끼워넣곤 하는데, 그 중 react-testing-library는 폭넓은 기능보다 정말 필요한 기능을 제공하며, state나 prop관리보다는 DOM에 어떻게 보여지는지 테스팅을 하기 위한 도구입니다. 이 testing-library에서 React-native를 위한 라이브러리도 제공해줘서 이 라이브러리를 사용하기로 했습니다.

1. RadioButton

테스트가 우선되어야 하기 때문에 먼저 컴포넌트는 로만 렌더를 시킵니다. 그 다음 RadioButton의 역할에 맞는 첫번째 테스트를 작성하였습니다. 뷰 관점에서 라디오버튼은 선택할 수 있는 동그라미와 라벨을 렌더시키는 역할을 하므로 이에 대한 테스트를 작성하였습니다.

먼저 임시 prop을 만들어주고, 그 prop을 넘겨받아 render하는 부분을 setup함수로 작성해줍니다. 이후 렌더부분과 관련한 첫번째 테스트 — 렌더를 하면 선택을 표기하는 동그라미 영역(selectButton)과 버튼이름 텍스트(label)부분이 존재하는지 — 를 작성합니다.

이제 위 테스트케이스를 성공하기 위한 컴포넌트를 다음과 같이 만들어보고 테스트를 실행해봅니다.

성공!했으면 이제 두번째 케이스를 작성합니다. 라디오 버튼의 두번째 역할은 클릭했을 때 prop으로 받은 함수를 실행시키는 것입니다. 라디오 버튼 특성상 선택되어있는 경우 다시 선택했을 때 취소가 안되고, 선택하지 않은 경우에만 선택이 되어야 하므로 이를 나타낸 테스트코드를 작성합니다.

jest.fn()이라는 mock함수를 이용해서, 값이 선택되지 않은경우(selected=false)에만 실행이 되는지 테스트케이스를 작성합니다. 그리고 다시 이 케이스를 성공시키기 위해 컴포넌트를 수정합니다.

2. RadioButtonList

이제 RadioButton컴포넌트들을 렌더시키는 리스트 컴포넌트의 테스트케이스를 작성해보겠습니다. 이것도 마찬가지로 버튼 리스트를 mock으로 만들고, 버튼의 text들이 렌더되는지, 버튼의 onPress가 잘 동작하는지 확인합니다.

위의 테스트케이스를 통과시키기 위해 props로 받은 button들을 렌더하고, 버튼이 클릭되었을때 함수가 호출되도록 prop으로 함수를 받아 넘겨주도록 작성하였습니다.

3. DeveloperMenu

DeveloperMenu는 RadioButtonList 컴포넌트와 하단의 저장 버튼을 렌더하고, RadioButton에 대한 상태값을 저장합니다. 또한, 선택이 안되어있는 라디오 버튼을 누르면 상태값이 변경되면서 라디오버튼 색깔이 바뀝니다. 따라서 렌더와 상태값 바뀌는 부분을 아래와 같이 테스트하였습니다.

이를 통과하는 코드를 다음과 같이 작성해 주었습니다.

4. Redux

API 모드를 관리하기 전역에서 관리하기 위한 리덕스 모듈을 만들 차례입니다. 이 모듈은 API모드 상태를 저장하고, API모드를 변경시키는 액션을 가지고 있으며, 액션함수에 담은 파라미터로 상태변경을 하는 리듀서를 가지고 있습니다.

리덕스 모듈의 경우 이렇게 간단한 값을 변경시키는 경우에 사용할 수 있는 보일러플레이트가 구성되어 있었기 때문에 따로 TDD를 진행하지 않았습니다. 위와 같이 리듀서모듈이 해야 할 역할만 정의해보고 보일러플레이트를 변형해 만든 다음, 리듀서부분만 테스트하는 코드를 작성하였습니다.

위와 같이 기본 initialState를 점검하고, 액션을 실행시켰을 때 state가 변하는 값이 있는지 테스트코드를 작성해서 확인하였습니다.

DeveloperMenu -> redux 연결

apiMode를 변경 & 저장하는 리듀서 모듈을 만들었으니, 이를 이제 DeveloperMenu컴포넌트와 연결시켜주어야 합니다. 먼저 리덕스와 함께 렌더되는지부터 체크해보겠습니다.

리덕스가 연결된 컴포넌트는 위와 같이 testing-library의 렌더함수를 이용하면 오류가 발생하므로 test코드에서도 store와 provider가 포함된 형태로 렌더를 시켜주어야 합니다. testing-library에서도 redux와 연결 가능하도록 제공해주는 기능이 있지만 저는 dispatch를 실행했을 때 action함수가 실행되었는지도 같이 test하고 싶어 redux-mock-store라는 라이브러리를 사용하였습니다.

기존의 테스트에서 렌더링 하는 함수를 setup으로 정의하였듯이 store와 함께 렌더하는 부분도 renderWithStore 함수로 정의하여 만들어주었습니다.

이제 redux를 DeveloperMenu에 연결시켜주기 전, redux state값을 렌더에서 확인할 수 있는 테스트코드를 작성합니다. store 값이 Production이면 두번째 radioButtonSelected 값의 배경색이 검정색이어야 합니다.

위 테스트를 통과시키기 위해 DeveloperMenu의 apiMode상태값의 초기값을 redux에서 가지고 온 apiMode 상태 값으로 변경해주면 테스트가 통과합니다.

다음으로는 하단의 저장버튼을 리덕스와 연결시킬 것입니다. 하단 저장버튼이 클릭되면 스토어의 apiMode를 변경하는 액션이 실행되어야 합니다. 이 역할을 위한 테스트코드를 다음과 같이 작성하였습니다.

먼저 선택이 되지 않은(배경이 흰색인) Production 라디오 버튼을 클릭하면 DeveloperMenu의 apiMode가 변경되어 배경색이 바뀌는 것을 확인합니다. 이후 하단의 저장 버튼을 누르고 액션이 의도한 바와 같이 ‘@setting/SET_VALUE’로 되는지 확인해봅니다.

이제 테스트작성은 모두 끝이 났으니 마지막으로 하단 버튼에 해당 액션을 실행시키는 함수를 만들어 넣어주면 됩니다.(handleSaveButtonPress)

이렇게 TDD로 개발자메뉴 구현해보기를 마쳤습니다. 테스트코드를 작성하는 것이 익숙하지 않은 상태에서 새로운 라이브러리를 이용해 작성해본 것이라 간단한 기능을 만드는데 시간이 꽤 오래 걸렸네요.

TDD가 처음이라 아직 어떻다 판단하기는 어렵지만 개인적으로 시도해보지않은 설계방법론을 적용해봄으로써 개인적으로 공부가 많이 되었습니다. 일이 바쁘다보면 구현위주로만 단편적으로 생각하고 코드를 작성해서 오히려 고치는 데에 시간이 많이 들어가는 데, 이런식으로 평소에 개발하는 습관을 들이면 좋을것 같다는 생각이 들었습니다. 또한 테스트코드가 있으니 컴포넌트가 어떤 역할을 하는지 파악하기가 더 수월해 기능이 복잡하면 복잡해질수록 더 도움이 되고, 리팩토링 할 때마다 테스트를 실행시킴으로서 예상하지 못했던 버그도 미리 발견할 수 있어 장점이 많은 방법론인 것 같습니다.

아쉬운 점이 있다면 이번에는 객체지향적 방법론을 역할과 책임중심 관점만을 위주로 적용하였는데, 다음번에는 다른 관점의 방법론들도 하나씩 추가해보아야겠다는 생각이 들었습니다. 이상으로 TDD 도전기를 마치겠습니다. 읽어주셔서 감사합니다 :)

[출처]

https://www.native-testing-library.com

https://velog.io/@velopert/tdd-with-react-testing-library

TDD 이야기(TDD에 대한 오해와 진실) - Rain.i TDD 를 현업에서 오랫동안 사용해온 경험많은 필자들이 TDD 적용을 어려워하거나 불필요하다고 생각하는 독자들을 위해 만든 ebook 이다.비록 작은 책이지만 TDD 가 왜 필요하고 어떻게 적용해 볼 수 있는지에…cloudrain21.com

Advanced testing in React Native with Jest: Redux integration This post is part of my series on unit testing with Jest in React Native. You can find the introduction here. By now…everyday.codes

Get to know us better! Join our official channels below.

Telegram(EN) : t.me/Humanscape KakaoTalk(KR) : open.kakao.com/o/gqbUQEM Website : humanscape.io Medium : medium.com/humanscape-ico Facebook : www.facebook.com/humanscape Twitter : twitter.com/Humanscape_io Reddit : https://www.reddit.com/r/Humanscape_official Bitcointalk announcement : https://bit.ly/2rVsP4T Email : support@humanscape.io

기업문화 엿볼 때, 더팀스

로그인

/