1월 원티드 챌린지에서 프론트 엔드에서도 테스트 코드가 필요하다는 것을 알게 되었다.
프론트 엔드에서 테스트 코드를 활용한 경험을 담은 기술블로그의 글을 읽다 보면 공통적으로 말하는 장점이 있다.
1. 오류를 빠르게 발견하고 수정할 수 있다.
2. 다른 라이브러리/프레임워크로 쉽게 리펙터링이 가능하다.
3. 로직의 흐름을 파악하기 쉬우므로 문서화 역할을 하며 새로운 팀원이 해석하기 쉽다.
여기에 추가적으로 TDD 방법론을 적용하였을 때 개발 시간이 줄어든다는 실제 사례를 <클린 아키텍처>에서 본 기억이 있다.
복잡한 비즈니스 로직이 포함된 코드가 아닌 간단한 사칙연산 수준의 코드를 구현함에 있어서도 위처럼 유의미하게 차이가 발생했다.
챌린지에서 배웠던 내용을 한마디로 요약하자면 "UI 테스트는 하지 않고, 요구사항의 사용자 시나리오에 집중하자"였다.
즉, UI 로직보다 비즈니스 로직에 더 중점을 두어야 한다는 것이다.
1. 테스트
그다음은 어떻게 테스트할지 고민할 차례인데, 팀/개인 프로젝트 진행 전에 알았으면 좋을 소프트웨어공학에서 공부했던 개념들이 나왔다.
조건의 중간값보다 다른 조건과의 경계에 있는 오류 발생 확률이 높은 경곗값을 테스트하거나, 정상적인 입력값과 비정상적인 입력값을 균등하게 테스트하는 기법등이 있었지만 프론트 엔드 실무에 적합한 테스트는 단정 테스트, 조건 검사라고 생각한다.
2. 단정 테스트
가장 작은 단위의 컴포넌트 혹은 함수를 테스트 하는 단위 테스트에서 단정 테스트를 사용할 수 있는데, 기댓값과 결괏값을 비교하는 것이다.
JavaScript에선 주로 Jest를 사용하는데 아래와 같이 활용할 수 있다.
test('two plus two is four', () => {
expect(2 + 2).toBe(4);
});
test('the shopping list has beer on it', () => {
const shoppingList = ['diapers', 'kleenex', 'trash bags', 'paper towels', 'beer'];
expect(shoppingList).toContain('beer');
});
test('the house has my desired features', () => {
const myHouse = {
bedrooms: 2,
bathrooms: 1.5,
kitchen: true
};
expect(myHouse).toEqual(expect.objectContaining({
bedrooms: expect.any(Number),
bathrooms: expect.any(Number),
kitchen: expect.any(Boolean)
}));
});
이러한 테스트를 통해 사용자가 input에 값을 입력하거나, 버튼을 클릭했을 때 해당 로직을 담당하는 함수 또는 컴포넌트가 제대로 호출되어 state가 업데이트되는지, 또는 axios/fetch 함수의 모의(mock) 버전을 사용하여 API 호출이 제대로 이루어지는지 테스트할 수 있다.
3. 조건 검사
true와 false를 반환하는 조건에서 테스트를 진행해야 할 필요가 있다.
아래의 사진처럼 true-true / false-false / true-fasle / false-true 사용자가 경험할 수 있는 모든 경우에서 테스트를 해야 한다는 것이다.
특히 프론트 엔드에선 특정 조건에 따라 컴포넌트, 태그 등을 변경해야 하는 경우가 생기는데 조건 검사를 통해 테스트를 할 수 있다.
작은 input 태그의 유효성 검사를 하거나, 로그인/비로그인 상태에 따라 보이는 컴포넌트가 다르고 관리자 권한이 있는 계정 로그인 시 게시물을 수정하거나 삭제할 수 있도록 하는 등 가장 필요한 테스트라고 생각한다.
4. V 테스트 모델
V 모델(V-model)은 소프트웨어 개발 프로세스로 폭포수 모델의 확장된 형태 중 하나로 볼 수 있다. 아래 방향으로 선형적으로 내려가면서 진행되는 폭포수 모델과 달리, 이 프로세스는 오른쪽 그림과 같이 코딩 단계에서 위쪽으로 꺾여서 알파벳 V자 모양으로 진행된다. V 모델은 개발 생명주기의 각 단계와 그에 상응하는 소프트웨어 시험 각 단계의 관계를 보여준다.
V 모델은 소프트웨어 개발의 각 단계마다 상세한 문서화를 통해 작업을 진행하는 잘 짜인 방법을 사용한다. 또한 테스트 설계와 같은 테스트 활동을 코딩 이후가 아닌 프로젝트 시작 시에 함께 시작하여, 전체적으로 많은 양의 프로젝트 비용과 시간을 감소시킨다.
- 위키백과
폭포수 모델, 애자일 모델, 나선형 모델 등 많은 방법론이 있지만 컴포넌트들로 이루어진 React 환경에서 V 모델의 단위 테스트 통합 테스트가 좀 더 적합해 보인다.
위에서 알아본 하나의 컴포넌트/함수 단위의 단위 테스트와 컴포넌트 간의 상호작용을 고려해 더 큰 규모의 테스트를 하는 것이 통합 테스트이다.
5. 프론트 엔드 실무 테스트 코드
실제 테크기업에선 어떤 방식으로 프론트 엔드에 테스트 코드를 적용하고 있는지 궁금해졌다.
첫 번째로 우아한테크의 기술블로그 글을 보았는데 사용자에 따라, 게시글의 상태에 따라 다른 결과가 출력되도록 모든 조건에서 시나리오를 작성해 둔 것을 확인할 수 있었다.
https://techblog.woowahan.com/8942/
// 마감일 / 마감일 수정 표기
1. 내부 사용자일 때
1-1. 마감일이 있을 때
1-1-1. 역분개를 했을 때
- DatePicker를 보여준다.
- 마감일을 수정할 수 있다.
1-1-2. 역분개를 하지 않았을 때
- 마감일을 텍스트로 보여준다.
1-2. 마감일이 없을 때
- '-'를 텍스트로 보여준다.
2. 외부 사용자일 때
2-1. 마감일이 있을 때
- 마감일을 텍스트로 보여준다.
2-2. 마감일 없을 때
- '-'를 텍스트로 보여준다.
// (이력 보기) 표기
1. 내부 사용자일 때
1-1. 변경이력이 있을 때
- '(이력 보기)'를 보여준다.
1-2. 변경이력이 없을 때
- 아무것도 보여주지 않는다.
2. 외부 사용자일 때
- 아무것도 보여주지 않는다.
이렇게 모든 시나리오(조건)에 따른 테스트 코드 작성과 리펙터링 시 발생한 장점에 대해서도 소개를 해주셨다.
1. 사용자 시나리오를 테스트하여 검증하기 수월하다.
2. 새로운 기능을 추가하기 쉽다.
3. PM팀과의 로직 질답이 쉽다.
1. 기존엔 관리자 계정, 고객 계정, 업주 계정을 직접 로그인해 테스트를 했는데 테스트 코드를 통해 원하는 조건에서 테스드가 가능해졌다고 한다.
2. 테스트 코드를 추가하니 컴포넌트 분리를 통해 간단해지고 기존 기능에 대한 안정성을 보장해 주니 새로운 기능을 추가하기 쉬워져 SOLID 원칙 중 OCP 원칙을 준수할 수 있다고 한다.
3. 특정 조건에서 어떤 기능이 가능한가요? 와 같은 PM의 질문에 답변하기 쉬워졌다고 한다. 예를 들어 VIP 고객 계정 로그인 시 A 게시판의 글을 수정할 수 있는지 질문이 들어왔을 때 시나리오 테스트 코드를 확인하면 바로 알 수 있기 때문에 가능해졌다.