1. 리덕스 테스트 흐름
리덕스 테스트는 어떻게 이뤄질까?
지난번에 우리가 리액트 앱을 테스팅 할 때는, 컴포넌트의 렌더링 결과물을 기록하고 이전과 지금 결과물을 비교하는 스냅샷 테스팅을 하는 방법도 알아보았습니다. 그리고, 컴포넌트 인스턴스의 메소드를 직접 호출을 해서 상태가 우리가 원하는 방식대로 바뀌었는지 검증하기도 했었고, 혹은 Enzyme 을 사용하여 DOM 이벤트를 시뮬레이트하여 특정 DOM 이벤트에 따라 우리가 지정한 함수가 제대로 호출되는지도 확인해보았습니다.
자, 그러면 리덕스 테스트는 어떻게 이뤄질까요? 일단 리덕스를 사용하게 된다면, 다음 항목들을 테스트 하게 됩니다:
- 액션 생성 함수
- 리듀서
- 컨테이너 컴포넌트
주어진 파라미터에 따라 우리가 원하는 액션 객체가 잘 만들어지는지 검증을 하구요, 특정 액션이 디스패치 되었을 때, 리듀서가 상태를 우리가 원하는 방식대로 업데이트 하는지 검증해야 합니다. 추가적으로, 이 모든것이 컴포넌트와 연동됐을 때도 잘 작동하는지 확인도 해주어야하죠.
여러분이 리액트 컴포넌트에 내장된 state 만을 사용하다가, 리덕스를 사용하게 되고, 그리고 또 리덕스에 익숙해졌을 때 어떤 점을 느꼈나요? 복잡한 상태 관리의 짐을 좀 덜어준다는 느낌을 조금이라도, 받으셨겠지요? 리덕스를 사용하게 되면서, 우리는 컨테이너 / 프리젠테이셔널 컴포넌트 구조를 사용하게 되면서, 프리젠테이셔널 컴포넌트를 만들 땐 단순히 컴포넌트가 어떻게 생겨야 할 지 구현하는 것에 더욱 집중 할 수 있게 됐고, 컨테이너 컴포넌트와 리듀서에서 상태 업데이트에 관련한 로직을 구현하는것에 집중 할 수 있게 됐었습니다.
테스트 코드 또한 마찬가지입니다. 상태를 위한 테스트와, 뷰를 위한 테스트를 분리하여 작성 할 수 있습니다.
따라서, 우리가 컴포넌트 테스트 코드를 작성 할 땐, props 가 전달되었을 때 올바르게 그려주는지 테스팅하고, 리덕스 관련 테스트 코드를 작성 할 땐, 우리가 만든 액션이 디스패치 됐을 때 상태가 원하는 형태로 변경되는지를 테스팅 해준다면, 모든 것이 잘 작동 할 것이라고 추측 할 수 있습니다.
하지만... 오류는 우리가 예상치 못했던 부분에서 발생하기 마련입니다. 이전에 했던 홍차 끓이기 비유를 다시 해보자면:
- 물 끓이기
- 티포트와 찻잔에 뜨거운물을 부어 데우기
- 찻잎 놓기
- 차 우리기
- 찻잔에 차 따르기
여러분이 이 1~5 에 대한 작업을 모두 제대로 할 줄 안다고 해서, 무조건 좋은 홍차가 나올 것이라고 보장할 수 있을까요? 꼭 그렇지는 않습니다. 예를 들어서 홍차의 찻잎을 우릴 때는 5분이 적당합니다. 어쩌다가, 5분 넘게 우리게 된다면 떫은 맛이 나서 결코 맛있는 홍차를 우릴 수 없습니다.
이렇게, 여러분이 예상치 못한 곳에서 에러가 발생 할 수도 있습니다. 따라서 우리는 한가지 테스트를 더 해주어야합니다:
1 ~ 5 까지 모두 진행하고나니 맛이 괜찮더라..!
이를 통합 테스팅 이라고 부릅니다. 유닛 테스트로 쪼갰던 모든 작업을 함께 하게 됐을 때 정말 잘 되는지 체크를 해주는 것이죠.
테스트를 어느 수준으로 해줘야 할까?
이건, 정해진 답이 없습니다. 저는 개인적으로, 방어적 프로그래밍을 한다면 테스트가 무조건 필요하지는 않다고 생각합니다. 다만, 테스트를 한다면 코드를 더욱 확실하게, 탄탄하게 해줍니다. 리액트 + 리덕스 앱의 테스트 커버리지는, 다음과 같이 하면 이상적이라고 생각합니다.
- 프리젠테이셔널 컴포넌트, props 에 따라 제대로 보여지는지 테스트
- 액션 생성함수, 우리가 의도했던 대로 액션을 잘 만들어주는지 테스트
- 리듀서, 상태와 액션을 전달해주면, 의도했던 대로 업데이트를 해주는지 테스트
- 컨테이너 컴포넌트, 통합 테스팅을 통하여 모든게 잘 되는지 테스트
하지만, 이상적일 뿐입니다. 회사에서 진행하는 프로젝트의 경우, 현실적으로 생각했을 때, 일정과 레거시 코드 등이 존재한다면 이상적인 형태를 인지하고 있어도 그 이상에 다가가기 힘들 때가 있습니다. - 저도 그렇습니다... ;( , 이는 시간과 여유가 해결해주는 문제입니다.
테스트를 작성한다는 것은, 개발 프로세스에 결국 한 단계가 더 추가 되는 것이기 때문에 개발 진행도를 아주 조금은 늦출 수는 있습니다 - 하지만 나중에 오류 때문에 고생 할 시간을 아껴주긴 합니다. 인력과 시간이 충분하다면 모든걸 하는게 맞긴 하겠지만, 그렇지 않을 경우엔 저는 다음과 같은 수준이 현실적 이라고 생각합니다:
- 프리젠테이셔널 컴포넌트 테스트, 이것은 사실 그렇게 공수가 크지 않습니다. (대부분의 경우)
- 액션 생성 함수, 저는 이 부분은 거의 테스트를 진행하지 않습니다. 조금 불필요하다고 생각합니다. 특히 FSA 를 따른다면 더더욱 그렇죠. (redux-actions 의 createAction 을 통해 액션을 만들게 되면 형태는 다 같습니다. 다양한 key 가 있는 것이 아니라, payload, error, meta 등의 키로 통일되어있죠.) 그 대신에, 비동기 액션 함수들은 테스팅을 해줍니다.
- 리듀서, 모든 액션이 다 제대로 작동하는지 확인해줍니다. 리듀서를 테스트하는것은 그렇게 복잡하지 않기 때문에 금방 할 수 있습니다.
- 시간이 많이 없다면, 그리고 1, 2, 3 번이 제대로 되어있다면, 통합 테스트의 경우 조금 간소하게 해줄 수도 있습니다. 예를 들어서, 컨테이너 컴포넌트의 버튼이 클릭 됐을 때, 스토어에 값이 제대로 바뀌었는지까지는 확인하지 않고, 그저 우리가 예상한 액션이 디스패치 되었는지만 확인해도 됩니다. 어짜피, 상태 관리의 영역은 리덕스 부분이니까요. 컴포넌트의 경우엔 주어진 값만 제대로 렌더링 하게 하면 됩니다. 물론 더욱 더 확실하게 하고 싶다면 상태값까지 모두 잘 바뀌는지 검증하는것도 나쁘지는 않습니다. 하지만 공수가 조금 더 들어갈뿐이죠.
테스트를 작성하게 될 때는, 진행 방향이 각자 다를 수 있겠지만 저는 일정이 타이트 할 경우 중요한 로직만 미리 테스트 해두고, 틈틈히 채워 나가는 것을 선호한답니다.
결국엔, 여러분, 그리고 여러분이 협업하게 될 팀의 재량 껏 하시면 됩니다 :)
자, 앞으로 이어질 튜토리얼에서는, 다양한 방식으로 리액트 + 리덕스 앱을 테스팅 하는 방법을 다뤄보겠습니다.