과거의 내가 미래의 나에게
프론트엔드 개발에서 테스트 전략 적용해보기(1) 본문
최근 제품에 대한 테스트 혹은 자동화 테스트의 중요성에 대해 평소보다 더 실감하고 있다. 기능이 추가되거나 버그의 수정이 이루지거나 혹은 리팩토링을 진행했을 시, 내가 기껏 만들어놓은 코드들이 정상 작용할 지에 대한 두려움은 항상 깔려있고 개발을 완료한 후 직접 하나하나 눌러보면서 간단한 알파테스트를 진행해보지만 동일한 화면에서 매번 같은 테스트가 진행되고 있지는 않은 것 같다. 한 화면에 대한 QC 리스트를 작성하여 이에 따라 테스트를 진행하면 되지만 기능의 추가수정이 있다면 QC 리스트도 같이 업데이트가 되어야할텐데 이러한 작업도 생각보다 공수가 드는 것이다.
테스트를 한다는 것은 서버에서 기능 단위로 작업한다고 생각하면 좀 더 쉽게 와닿았다. 하지만 화면을 그리는 프론트단은 어떤식으로 테스트를 하는 것인지 감조차 잡히지 않았는데, 이번에"프론트엔드 개발을 위한 테스트"라는 책을 구매하여 어떤식으로 테스트를 진행하는 것인지 맥락을 잡고 우리 프로젝트에는 어떻게 적용시킬지에 대해 고민해보는 시간을 가져보겠다.
테스트가 필요한 이유
개발을 하다보면 만들어진 컴포넌트에서 추가로 작업하거나 혹은 리팩토링이 필요한 경우가 있다. 이를 위해 코드를 수정하는 것은 어렵지 않지만 코드를 변경함으로 인해 어떤 버그가 발생할 지 모르는 상태가 된다. 이를 방지하기 위해 코드를 수정한 후 직접 클릭해보며 테스트를 해보지만 비용이 많이 발생하게 된다.
이 때 테스트 코드가 있다면 기 좀 더 빠르게 코드에 문제가 없는지 확인할 수 있게 된다. 테스트를 마련해놓으면 빠르게 안정된 코드를 생산해낼 수 있을 뿐만 아니라 이를 자동화시키면 더욱 더 견고한 환경을 만들 수 있을 것이다.
내가 코드를 직접 수정하는 경우도 있지만 라이브러리들이 업데이트 되는 경우에도 문제가 발생할 수 있다. 이러한 라이브러리들의 변경점들을 depandabot으로 의존성을 관리하기도 하지만 테스트 코드를 미리 작성해놓으면 더 안정된 코드 환경을 마련할 수 있다.
테스트 코드는 하나의 안내서이기도 하다. 코드가 어떤 기능을 제공하고 어떤 방식으로 작동하는 지 쉽게 파악할 수 있고 또 이 테스트들을 통과하면 안내서대로 코드가 작성되어있음을 누구라도 쉽게 알 수 있게 될 것이다.
테스트 방법의 종류
1. 함수 단위 테스트
함수 단위마다 올바르게 작동하고 있는지 테스트를 작성할 수 있다. 자동화로써 관리할 수 있다.
2. UI 컴포넌트 테스트
UI 컴포넌트를 기준으로 진행하는 테스트이다. 어느 범위로 테스트하냐에 따라 그 종류가 다르다.
1) 단위테스트: UI 컴포넌트마다 단위 테스트
2) 통합테스트: 비동기 응답에 의한 화면 갱신 등 외부 요인을 포함한 테스트
3) 시각적 회귀 테스트: CSS 적용된 출력 결과 검증
3. E2E 테스트
브라우저 고유의 API를 사용하거나 화면 간 테스트가 필요할 때 진행하는 테스트이다. 거의 실제에 가까운 상황 재현하여 테스트 가능하다.
< 라이브러리와 도구 >
테스트를 할 수 있는 도구들이 이미 시중에 많이 출시되어있다. 책에서 소개한 몇 가지 사례만 적어놓는다.
1) 타입스크립트: JS의 타입 문제를 해결해줌으로써 JS의 품질 향상
2) Jest: CLI 기반 테스트 프레임워크 및 테스트 실행기
3) 플레이라이트: 테스트 프레임워크 및 테스트 실행기
4) reg-suit: 시각적 회귀 테스트 프레임워크
5) 스토리북: UI 컴포넌트 탐색시
테스트 범위의 종류
1. 정적분석
타입스크립트나 ESLint가 제공하는 기능을 활용한다. 타입스크립트를 통해 타입을 추론하여 코드 안정성을 도모하고 ESLint를 통해 부적절한 구문을 수정해서 버그를 사전에 방지한다. 버그를 조기에 발견해줄 수 있도록 해주며 각 모듈의 내부를 검증하고 모듈끼리 연계하여 사용할 때의 문제점도 검증한다.
2. 단위테스트
한 가지 모듈에 한정하여 해당 모듈이 제공하는 기능을 검증하는 테스트. UI 컴포넌트 모듈에서는 입력값과 출력값이 어느정도 정해져있기에 테스트하기 쉽다.
거의 발생하지 않는 케이스(코너 케이스)에서 예외를 처리하는 것도 있는데 이는 거듭된 생각을 통해 미처 고려하지 못했던 부분을 발견할 수 있다.
3. 통합테스트
모듈들의 조합으로 제공하는 기능을 검증하는 테스트이다. 범위가 넓어진만큼 상대적으로 대략적인 검증에 그치게 된다. 예를 들어 버튼 컴포넌트, 폼 컴포넌트 등의 단위들이 있고 이 기능을 시작하는 버튼을 누름으로써 폼에서 작성되는 등 하나의 기능을 테스트하는 것이다.
4. E2E테스트
UI 테스트뿐만 아니라 외부와의 작업도 포함된 테스트이다. 거의 실제 결과에 가깝게 테스트하게 된다.
테스트 목적의 종류
1. 기능 테스트
프론트엔드의 주요 작업은 UI 조작하여 상태가 변하고 이를 화면에 반영하는 것이다. 따라서 프론트엔드에서는 기능 테스트로 상호작용 테스트를 많이 진행하게 된다. 상호작용 테스트는 사용자와 웹 브라우저에서의 상호작용을 테스트하는 것으로 사용자가 특정 동작을 수행하면 정상적으로 반응하는지 중점을 둔다.
< 헤드리스 모드 >
보통 기능을 테스트하려면 브라우저를 실행하여 눈으로 보면서 직접 클릭하고 반응을 보는데, 이렇게 하지 않고 화면 없이 백그라운드에서 브라우저를 실행하여 사용자의 상호작용을 시뮬레이션을 할 수 있다. 이를 헤드리스 모드라 하는데 이렇게 하면 그래픽을 렌더링할 필요가 없으므로 테스트 속도가 더 빠른데다가 자동화하여 CICD 환경에서 테스트를 할 수도 있게 된다
2. 비기능 테스트
접근성 테스트가 그 대표적인 것으로, 말그대로 기능이 아닌 품질과 관련된 비기능에 관련하여 진행하는 테스트이다.
3. 시각적 회귀 테스트
변경 전의 스크린샷과 변경 후의 스크린샷을 찍어 픽셀 단위로 비교한 후 차이가 있다면 이를 보고한다고 한다. 기능이나 UI의 변경이 일어나도 시각적으로 보이는 화면이 그대로 유지되고 있는지 확인하는 과정이다.
UI가 변경된 후 기존의 디자인이나 레이아웃에 의도치 않은 시각적 오류가 발생하지 않았는지 확인하는 테스트 방법이다. 기능이 업데이트 되거나 코드가 수정되는 등의 작업 후에도 화면에 변화가 없는지 확인할 때 유용하다.
테스트 전략 모델 및 전략 계획 사례
테스트의 종류는 많고 이를 수행하는 방법도 다양하다. 모두 시행하면 좋겠지만 이는 결국 비용이 발생한다. 따라서 각 상황에 가장 적합한 방법을 찾아 적용할 수 있어야 할 것이다. 아래는 이러한 테스트 전략을 나열하고 그 사례를 적어놓았다.
1. 아이스크림 콘
단위 테스트같은 작은 범위의 테스트 비율이 낮고 E2E 테스트 같은 큰 범위의 테스트가 높은 아이스크림 콘 전략 모델이 있다.
2. 테스트 피라미드
테스트가 적고 작은 범위의 테스트 비율이 높은 테스트 피라미드 전략 모델이 있다.
3. 테스팅 트로피
이는 테스트 중 통합테스트의 비중이 가장 높아야한다는 전략 모델이다. 프론트 개발에서는 단독으로 시행하는 기능은 거의 없다. 보통 전부 유기적으로 연결되어 다양하게 작동되기 때문이다. 테스팅 트로피에는 사용자 조작을 기점으로 한 통합테스트의 비중이 높을수록 더욱 우수한 전략이라고 한다.
상황에 따라 다르겠지만 책에서는 아이스크림 콘 전략 모델을 가장 지양한다. E2E는 거의 실제 제품처럼 실제로 구현해봄으로 테스트에 통과되면 거의 완성된거나 다름없겠지만, 워낙 큰 범위의 테스트이기에 한 번 실행하면 테스트 시간이 너무 길어진다. 테스트 시간이 길어지면 테스트를 상대적으로 덜 하게 되고 이는 결국 테스트의 부재가 야기된다.
마무리
테스트의 방법은 때에 따라 무수히 많을 것이고 이를 필요로 할 때 마다 추가한다면 테스트 범위가 중복 되거나 혹은 너무 많은 시간이 걸릴 수도 있을 것이다.
테스트의 목적은 더 안정적으로 빠르게 제품을 만들어내고 유지관리하는 데에 있다. 따라서 무작정 테스트 전략을 따라하기 보단 내가 개발하고 있는 환경에서는 어떤 테스트가 있으면 더 나을지 고민하고 전략을 세워야 할 것이다.
예를 들어 반응형으로 제작된 프로젝트를 다루고 있을 때는 하나의 HTML을 다루고 있으므로 변경 시 다른 화면이 영향이 없는지 확인하는 것은 필수일 것이다. 이 때 도움이 되는 것은 시각적 회귀 테스트일 것이다.
이와 같이 자신이 목적으로 하는 것을 명확히 하고 이에 따른 테스트 전략을 고민해봐야할 것이다.