해당 포스트는 주관적인 내용이 많이 담겨 있습니다.
별도의 사용법은 다루지 않습니다.
블로그를 리팩토링하기 위해 먼저 기존 코드에 대한 테스트 코드를 작성하기로 하였다.
고민
테스트 코드를 작성하면서 Jest 는 회사에서 어느 정도 사용해 봤기 때문에 크게 이슈 사항이 없었는데 React Testing Library(RTL) 의 경우는 해당 리팩토링 과정에서 처음 적용해 보는 거라 라이브러리를 사용하는 방법부터 어떤 식으로 테스트 코드를 작성해야 하는지에 대한 점들을 많이 고민해 보고 찾아본 것 같다.
element 선택
라이브러리 사용법부터 보자면 컴포넌트에 대한 테스트의 경우 컴포넌트 특성상 UI 를 기반으로 테스트를 진행하기 때문에 element 를 선택해야 하는 경우가 많다.
RTL 의 경우 element 를 선택하는 query 메서드가 여러 개 존재하고(e.g.
getByRole
, getByText
) JS 선택자도 사용 가능하다. 여기서 일단 어떤 것을 사용해야 하는지에 대해 의문이 많이 생겼다.이 의문에 대해서는 테스트 코드를 작성해 보면서 어느 정도 기준을 잡을 수 있었다.
나 같은 경우 JS 선택자 사용을 최대한 지양하여 query 메서드를 사용하기로 하였다.
만약 className 이나 UI 구조 등으로 JS 선택자를 통해 element 를 선택하는 경우, 실제 코드에서 className 이 필요 없어져 className 을 제거하거나 UI 구조를 변경해야 한다면 그때마다 테스트 코드도 같이 변경해야 하기 때문에 유지 보수가 어려워지고 테스트 코드에 대한 신뢰성을 잃게 될 수 있다고 생각하기 때문에 사용을 지양하기로 하였다.
테스트 케이스 이름 작성
테스트 케이스의 메시지를 작성할 때 짧고 간결하게 작성하는게 맞을까? 아니면 상세하게 작성하는게 맞을까?
등의 고민을 했었다.
여러 블로그를 찾아보고 생각한 답은 “내가 아닌 다른 사람도 파악이 가능하도록 작성하자” 였다.
다른 사람은 개발자도 포함이고 비개발자도 포함이다.
그래서 메시지를 작성할 때 최대한 풀어서 작성하려고 노력했다.
중복되는 코드
테스트 코드를 작성하다 보면 중복되는 코드가 여러 번 발생한다.
예를 들어
Button
컴포넌트의 텍스트 표시 테스트와 className 적용 체크에 대한 테스트를 하는 경우 이런 식으로 테스트 코드를 작성할 수 있다.이때 버튼을 선택하는
button
변수에 중복이 발생한다.해당 예제는 하나의 변수만 중복되지만, 복잡한 컴포넌트 또는 함수인 경우 많은 중복이 발생할 것이다.
이 부분에 대해서 고민하고 찾아보니 테스트 코드를 작성할 때에는 DRY 보다는 DAMP 를 따르는 것이 더 중요하다고 생각했다.
DRY: Don't Repeat Yourself DAMP: Descriptive And Meaningful Phrases
요약하자면 DRY 는 “중복을 최소화 하자” 이고 DAMP 는 “이해하기 쉽게 작성하자” 이다.
위의
Button
컴포넌트도 beforeEach
등을 통해 button
변수에 element 를 할당하여 중복을 제거할 수 있지만 문제점이 발생한다.첫 번째로
beforeEach
를 통해 중복을 제거한 경우, 테스트 케이스를 파악할 때 해당 테스트 케이스뿐만 아니라 beforeEach
도 같이 파악을 해야한다.두 번째로 해당 컴포넌트에 추가적인 테스트를 진행하는 경우에 문제가 발생한다.
만약 해당 테스트 케이스처럼
onClick
props 를 적용하여 클릭 이벤트에 대한 테스트를 진행하는 경우, beforeEach
에 이미 컴포넌트 render()
가 실행되었기 때문에 해당 테스트 케이스에서는 버튼이 2번 출력되어 클릭 이벤트를 테스트하려는 버튼을 찾을 수 없는 이슈가 발생한다.이렇게 이슈가 발생하는 상황들을 경험하고 테스트 코드가 중시하는 DAMP 원칙을 바탕으로 중복 없는 코드보다는 이해하기 쉬운 코드를 목표로 테스트 코드를 작성해야겠다는 생각이 들었다.
전역 설정 사용 유무
만약
config
라는 변수가 있고 Footer
컴포넌트에서 해당 config
를 import 해서 사용한다고 해보자..name 정보를 검증하는 테스트 케이스가 있을 때 2가지 방법으로 검증을 할 수 있다.
케이스 1은 컴포넌트 내부에서 사용 중인
config
변수를 import 해서 그대로 검증하고 있고 케이스 2는 config
변수의 실제 값을 통해 검증하고 있다.테스트 코드 작성 초반에 케이스 1은 유지보수 측면에서 장점이 있지만 테스트 코드 파악 시
config
도 같이 파악해야 한다는 단점이 있고 케이스 2는 테스트 코드에 대한 가독성 측면에서 장점이 있지만 config.name
값이 수정될 때 마다 테스트 코드를 같이 수정해 줘야 한다는 단점이 존재했기 때문에 어떤 방식으로 설정해야 더 좋은 방법일까에 대한 고민을 했었다.고민 끝에 검증해야 하는 값이 비즈니스적으로 중요하거나 자주 변경되지 않는 값인 경우에는 케이스 2 방법을 적용하고 상대적으로 덜 중요하거나 자주 변경하는 경우에는 케이스 1 방법을 사용하기로 하였지만 더 좋은 방법이 떠올랐다.
바로
jest.mock()
을 통해 mock 데이터를 적용하는 방법이다.이 방식을 통해 실제
config
변수의 값을 파악할 필요도 없고 config.name
값을 수정해도 테스트 코드를 같이 수정할 필요가 없어져서 케이스 1, 2 에 대한 단점들을 해결할 수 있다.페이지 단위의 테스트 여부
“페이지 단위 컴포넌트(Next.js - Pages Router)도 컴포넌트니까 테스트를 진행해야 하나?” 라는 고민을 했었다. 이 고민에 대해서는 페이지 단위는 사용자가 실제로 보는 화면이기 때문에 E2E 테스트가 더 적합한 테스트라고 생각했다.
아쉬움
mock 데이터를 통해 함수 또는 컴포넌트를 테스트하는 경우 개인적인 아쉬운 점이 있었다.
예를 들어 포스트 아이템인
postItem
이라는 데이터가 존재하고 여러 개의 컴포넌트에서 동일한 데이터를 사용한다고 해보자.이때
A
컴포넌트를 테스트하기 위해 postItem
데이터에 A
컴포넌트에 필요한 mock 데이터를 적용하였다.그 이후
B
컴포넌트를 테스트하기 위해 postItem
데이터에 B
컴포넌트에 필요한 mock 데이터를 적용하였다. 이로 인해
postItem
데이터의 수정을 여러 번 진행해야 했다.해당 이슈는 아마 테스트 코드에 대한 경험 부족과 프로젝트 특성상 데이터를 API 로 불러오는데 응답 값이 매우 복잡한 구조로 되어 있어 발생한 누락 이슈라고 생각한다.
다음에 테스트 코드를 작성하는 경우 mock 데이터를 확실하게 체크 후 테스트 코드를 진행해야겠다는 생각이 들었다.
도입 장점
- 테스트 코드를 적용하기 전에는 코드를 테스트할 때 실제 개발 환경에서 import 를 통해 불러온 다음 테스트를 진행했는데 이 과정에 대한 시간 비용을 절약할 수 있을 것 같다.
- 테스트 코드만 확인해도 어떤 기능을 하는지 바로 파악할 수 있었다.
- 기존 코드를 수정한다는 불안감을 어느 정도 해소해 줘서 리팩토링을 마음 편하게 할 수 있었다.
도입 단점
- 처음 Jest, RTL 을 통해 테스트 코드 작성 시 약간의 러닝커브가 존재했다.
- UI 같은 자주 변경 될 수 있는 코드가 변경되면 테스트 코드도 같이 변경해야 해서 작업량이 조금 증가했다.
후기
테스트 코드를 많이 작성해 보지 않아서 기본적인 이슈도 있었고 적용한 사항들이 올바른 방법이 아닐 수 있지만 테스트 코드를 작성하면서 많은 생각을 할 수 있었다.
그리고 실제 경험을 통해서 글로만 읽었던 테스트의 장점들을 더 명확하게 확인할 수 있었다.
추후 프로젝트 진행 시에도 테스트를 적용할 생각이다.
참고