테스트에 대한 필요성은 이전부터 꾸준히 제기되어 왔습니다. 여러 이유가 있지만 가장 큰 이유는 제품의 안정성이 제일 큽니다. 라이브러리를 업데이트하거나 큰 기능의 변화가 있을 때, 주요 기능이 제대로 동작하는 것을 일일이 확인하는 것은 큰 리소스가 듭니다. 더불어 제대로 동작하는 지 확인하기 어렵습니다.
동료들이 모여서 일주일에 한 번씩 모여 이 문제를 해결하기로 했습니다.
개발에서 테스트까지 QA를 거치면서 QA하는 분들의 리소스가 점점 부족했습니다. 그래서 e2e 테스트를 해보는 것을 제안했고 동료들도 이를 좋게 생각했습니다.
저는 리액트 네이티브에서 할 수 있는 e2e 테스트를 해보기로 했고 detox를 이미 시도한 흔적이 있어서 경로를 최신화하고 라이브러리 모킹도 다시 해서 살렸습니다. 시뮬레이터에서 짠 시나리오대로 화면이 넘어가는 것이 너무 신기했습니다!
하지만 애뮬레이터에서 동작하다보니 느린 면이 있고, QA를 대체하는 건 SaaS로도 해결할 수 있다는 피드백을 받아들여 e2e 테스트는 잠시 내려놓기로 했습니다.
e2e 테스트를 해보면서 다들 테스트나 세팅 경험이 전무한 상태에서 시작하는 것이 어렵단 의견이 모아졌습니다. 일단 한 보 후퇴해서 Unit Test부터 천천히 쌓아보기로 했습니다.
자주 쓰는 공통 유틸은 자주 바뀌진 않지만 테스트를 공부하기엔 좋은 기회라고 생각했습니다. 이번엔 웹 애플리케이션 내에서 유닛 테스트를 시도해보기로 했습니다.
Next.js안에서 이미 jest에 대한 설정이 있었기 때문에 이번엔 jest를 사용해보기로 했습니다.
jest는 프로젝트 내에서 이미 세팅되어 있었지만 막상 테스트 코드를 작성하려니 어떻게 작성해야 하는가?에 대한 고민이 있었습니다. 그래서 테스트 코드를 작성할 수 있는 여러 방법을 고민했습니다. 찾아보니 크게 두 가지 패턴이 존재했습니다.
말 그대로 동작되어야 하는 기능에 대해 작성한 경우입니다. 흔히 이 패턴을 가장 많이 사용할 것 같습니다.
describe('calcualteInterest()', () => {
it('1000원에 이율이 10%라면 이자는 100원이 되어야 한다.', () => {
const currentMoney = 1000
const interestRate = 0.1
const interest = calcualteInterest(currentMoney, interestRate);
expect(interest).toBe(1100);
});
테스트 코드를 위 3가지 기준으로 나누어 작성합니다.
1. Given: 이미 주어진 값. 시스템이나 시나리오에서 변하지 않는 값으로 생각합니다.
2. When: 시나리오 또는 시스템 안에서 변화할 수 있는 값을 의미합니다.
3. Then: Given과 When의 조합으로 나타나는 테스트 결과를 나타냅니다.
위 코드를 이 패턴에 대해서 다시 작성하면 아래와 같이 작성할 수 있습니다.
//GIVEN
describe('1000원이 있습니다.', () => {
//WHEN
it('이율을 10%로 했을 때', () => {
const currentMoney = 1000
const interestRate = 0.1
const interest = calcualteInterest(currentMoney, interestRate);
expect(interest).toBe(1100);
});
});
Then은 추가로 작성하지 않았습니다. Then까지 이 코드에 포함 시키면 중첩이 일어나 더 코드를 복잡하게 할거라 생각했습니다. 대부분 테스트의 결과값은 주어진 조건만 보고 예측할 수 있다고 생각하기 때문입니다.
정답은 없고 지금은 테스트 코드에 대한 컨벤션을 정하는 시간이 아니었기 때문에 2번을 택하기로 했습니다. 제가 제시했던 2번의 장점은 다음과 같습니다.
1. 테스트 시나리오 변경 시 변경점이 정해져있기 때문에 찾기 쉽다.
2. 함수에 의존성이 많아지는 경우 변경점이 발생할 수 있는 경우도 많아지는데 이를 체계적으로 관리할 수 있습니다.
변경점이 많아지는 경우 단순한 설명이 나을 수도 있다. depth가 깊어져서 코드를 읽기 어려울 수 있습니다.
테스트에서 빠질 수 없는 것이 Mocking이었습니다.
Mocking이란?
'가짜'라고 보면 좋을 것 같습니다. 실제 라이브러리 대신 가짜로 만든 모듈을 이용하거나 실제 API 호출 대신 미리 지정해둔 값을 받아 테스트에 이용할 수 있습니다.
Mocking을 하는 이유는 의존성을 제거하기 위함입니다. 테스트를 하려는 그 모듈에만 집중하기 위해 의존하는 나머지 요소들에게선 정확히 값이 들어온다는 보장이 필요합니다.
jest의 mocking에는 크게 3가지가 존재합니다. jest.fn
, jest.mock
, jest.spyOn
이 있습니다.
1.jest.fn
: 함수를 모킹할 때 사용합니다.
2.jest.mock
: export한 객체와 내부에 있는 모든 요소들을 mocking합니다.
3.spyOn
: 해당 함수의 호출 여부와 어떻게 호출되었는 지를 알고 싶을 때 사용합니다(내부 구현은 그대로 유지합니다).
spyOn까지 사용해보진 않았습니다만, side-effect가 발생할 수 있는 모듈을 테스트할 때 좋을 것 같다는 생각이 듭니다.
갑작스러운 퇴사로 인해 컴포넌트 테스트까지 진행하지 못한 점이 아쉽습니다. 컴포넌트 테스트같은 경우는 앞으로 개인적인 공부를 통해 해봐야겠습니다.