프론트엔드 테스트의 모든 것
저와 같은 사람이라면 개발 워크플로우에서 테스트를 자동화하여 원치 않는 사이드 이펙트를 줄이고 애플리케이션의 품질을 향상하는 것이 중요하다고 생각하겠죠
하지만 다양한 테스트 라이브러리들이 있는데 그중에서 뭐가 쓰는 게 맞는지, 각 라이브러리들을 조합해서 어떤 테스트를 할 수 있는지 파악하기가 어렵습니다
그래서 프론트엔드 테스트에 대해서 더 자세히 조사하고 블로그 글로 정리하려고 합니다. 이 글에선 프런트엔드 테스팅 시 자주 사용되는 라이브러리, 용어를 정리하고, 코드 스니 펫을 공유해 드립니다.
해당 글의 내용은 테스팅에 대해서 깊게 다루지 못한 프론트엔드 초급/중급 개발자를 중심으로 합니다.
우선, 테스트 유형들은 다양하게 진행해 볼 수 있는데, 프론트단에 주로 아래 3가지 테스트들을 진행합니다
- Unit Tests (단위 테스트): 최소 단위의 util 함수, 커스텀 리액트 hook, 아니면 하나의 컴포넌트를 테스트합니다
- Integration Tests (통합 테스트): 어떤 어플리케이션에서 여러개의 요소들이 같이 돌렸을 때 어떻게 동작하는지 확인하는 테스트합니다
- End-to-end Tests (E2E): 어떤 사용자가 웹앱을 사용하는 것처럼 시뮬레이션 돌린다 (이때 서버, 인프라, 웹앱 모두 테스트하게 된다)
이번 글에서는 단위 테스트와 통합 테스트에만 집중하고 E2E 테스트는 생략하겠습니다
테스트 관련 용어 정리
- Spy는 어떤 함수를 실행 여부 기록하고, 함수 실행할 때마다 그의 응답 값, Argument 등을 기록해 줍니다 (보통 “이 액션을 통해서 내가 원하는 함수가 실행됐을까?” 성격의 테스트를 할 수 있습니다)
- Stubbing 같은 경우에는 어떤 함수를 스파이하면서, 그 함수의 로직을 오버라이딩 시킬때 stubbing이라고 볼 수 있습니다 (보통 어떤 컴포넌트의 의존성으로 인한 사이드 이펙트의 양을 줄이기 위해 단위 테스트에 주로 사용됩니다)
- Mock은 Stubbing과 같이 어떤 함수의 implementation을 대체하거나, 그 함수가 응답 값이 원하는대로 변경할 수 있습니다
- Assertion이라는 개념을 쉽게 생각한다면 “내가 원하는 결과를 받고 있을까?” 확인해 주는 기능인데요. Assertion 라이브러리들은 변수 (객체, 배열, string, number)가 쉽고 가득성 좋게 비교할 수 있는 utility 함수들을 제공해 줍니다
- Snapshot 테스트는 어떤 컴포넌트가 렌더링될 때, 어떤 아웃풋이 나오는지 파일로 기록해 줍니다. 만약에 코드 수정 후에 렌더링되는 아웃풋이 변경되면 테스트가 실패하게 됩니다 (보통 “내가 로직만 수정했는데 DOM이 왜 바뀌는거지?”라는 상황을 예방하기 위해 사용합니다)
- Instrumentation이라는 개념도 있는데요. 테스트 코드 커버리지를 계산하려고 내 코드베이스 전체 라인에서 몇줄이 실행 되는지, 각 라인에서 몇번 실행되는지 측정하기 위해서 모든 파일을 wrapping해야 합니다. 이 과정이 code intrumentation이라고 합니다. 보통 babel 설정에서 istanbul 라이브러리로 wrapping합니다 (주의: production 환경에 instrumentation을 절대 쓰지 마세요!!)
유닛/통합 테스트를 위한 요소들
Test Launchers
테스트를 실행하면 실행할 수 있는 툴이 있어야겠죠? Test Launcher들은 CLI나 Node.js를 통해 테스트 수행하고, jsdom이나 브라우저상에 테스트실행할 수 있게 합니다
jest
,mocha
,karma
,TestCafe
,jasmine
Structure Providers
테스트 작성할 때 가득성 좋게 테스트 케이스 조합, 순서를 맞출 수 있도록 global 함수를 제공해 줍니다 (describe()
, test()
, it()
)
jest
,mocha
,TestCafe
,jasmine
Assertion Libraries
위에 설명한대로 어떤 테스트가 원하는 결과를 받고 있는지 util 함수를 제공해 주는 라이브러리들 입니다.(expect()
)
jest
,chai
,TestCafe
,jasmine
Mocks, Spies, Stubs
sinon
,jest
,jasmine
컴포넌트 렌더링 라이브러리
리액트에서 컴포넌트 렌더하고, 렌더링된 컴포넌트에서 원하는 element를 찾을 수 있도록 selector 제공해 주고, element에서 다양한 이벤트 (onChange, click, 등)를 호출할 수 있습니다 (testing-library는 리액트뿐만 아니라 여러 프레이워크를 지원합니다)
enzyme
,react-test-renderer
,@testing-library
,@vue/test-utils
Coverage Reporting
테스트가 총 코드베이스의 얼마 만큼 커버하고 있는지 리포팅하는 것입니다
istanbul
+ (mocha
||jest
||karma
||jasmine
)
가장 유명한 라이브러리 바탕으로 어떤 기능들을 제공하는지 정리한 표예요!
Jest + Enzyme 조합으로 테스트 작성하기
명확한 예시를 전달드리기 위해 간단한 컴포넌트를 제작해 봤는데요
본 컴포넌트는 아래와 같은 스펙을 가지고 있습니다:
- input이 수정 시 props에서 받은 onChange 이벤트 핸들러 호출
- button 클릭 시 콘솔에 로그 찍고 props에서 받은 onSubmit 이벤트 핸들러 호출
- Redux store에서 받은 testString 값이 화면에 노출
우선 제일 간단한 snapshot 테스트부터!
Enzyme에서 shallow 통해 렌더링하고, jest에서 제공하는 toMatchSnapshot()
라는 assertion을 통해 snapshot 테스팅 완성합니다
이벤트 핸들링은 어떻게 하나?
useSelector에서 응답해 주는 값이 보여주고 있나?
해당 프로젝트를 Codesandbox에서 확인해 봐요! (비고: codesandbox 자체 버그 때문에 로컬에서만 테스트가 성공합니다 😢)
원티드에서 현재 다양한 포지션을 채용하고 있습니다.