테스트를 처음 공부하는 프론트엔드 개발자의 개념 다지기

HyunHo Lee·2022년 6월 7일
14

테스트 코드

목록 보기
1/5
post-thumbnail

테스트에 눈이 간 이유

백엔드 개발자들은 API를 설계하면서 컴포넌트, 컨트롤러, 서비스 등 구현한 것들에 대한 거의 모든 테스트 코드를 설계한다. 하지만 프론트엔드 개발자는 밴엔드 개발자처럼 무조건 테스트를 위한 코드를 짜는것 같지는 않았다. 그 이유가 무엇일까?

나의 경우는 일단 여러가지 스택을 익히고, 프로젝트를 설계하는 것으로도 바빳다. 그리고 어디선가 주워들은 '프론트엔드에서 TDD는 어렵다'라는 말을 듣고, 테스트 코드 설계 자체를 하지 않게 되었고 정당화 했다. 그렇게 테스트 코드없이 프로젝트를 설계하다가 프론트에서 많이 사용한다는 jest, react-testing-library, cypress, enzyme에 대한 기본 개념조차 없는 나를 발견하고 정신을 차렸다. 프론트엔드에도 유용한 테스트 도구들이 등장하였고, 레퍼런스도 많아져서 충분히 테스트가 가능해졌다. 만약에 나와 같이 테스트에서 도망친 프론트엔드 개발자가 있다면, 이 글을 보고 지금이라도 프론트엔드에서의 테스트에 대한 개념을 시작으로 테스트에 대해 공부해보면 좋을 것 같다.

오늘은 테스트 코드를 작성하는 방법보다는 프론트엔드의 테스트에는 어떤것들이 있고, 상황에 맞게 무슨 테스트를 하면 좋을지에 대한 글을 알아보자.


테스트란?

사실 우리는 테스트는 언제나 하고있다. 테스트 코드를 안 짤뿐...

예를들어, 디자이너가 준 피그마에 맞게 UI를 구현하며 브라우저를 확인하고 UX가 좋은지 체크하며 데이터가 정상적으로 받아져 오는지 console.log로 확인하는 등의 행위를 한다. 이 외에도 버튼을 클릭했을 경우 동작하는 기능들도 우리는 구현한 다음 직접 눌러본다.

만약에 기능이 매우 많거나 코드를 수정했는데 해당 기능과 관련된 다른 다양한 기능들이 있다고 가정해보자. 리팩토링 조금 한걸로 이 모든것에 대한 테스트를 계속 하게 된다면 매우 비효율적일 것이다. 그렇다고 리팩토링을 안하자니 기술 부채는 증가할 것이고 악순환이 된다.

테스트 코드를 작성하면 적극적인 리팩토링이 가능해지며 결함을 예방하고 요구사항을 충족하는지 검사할 수 있다. 그리고 테스트 코드가 명세서의 역할을 하여 코드를 파악하는데 도움을 주기 때문에 협업에서도 유용하다. 더 나아가 테스트 자동화로 반복적인 테스트에 대한 비용이 줄어든다.(물론 테스트 코드를 작성하는 비용이 있지만 말이다.)


테스트 코드를 작성하는것은 필수가 되고 있지만, 모든 부분에서 하는것은 비효율적이다. 프로젝트를 설계해야하는데 테스트 코드만 짜고 있을 수는 없기 때문이다. 테스트의 기회비용을 따지면서 중요한 곳에만 테스트를 하는것이 포인트가 될 것이다.


프론트엔드 테스트 종류

정적 테스트

코드를 실행시키지 않고 테스트를 수행한다. 정적 테스트는 이미 많은 사람들이 사용하고 있을 것이다. 타입을 체크하기위해 typescript를 사용하고, 컨벤션과 같은 코드 스타일을 검증하기 위해 eslint 등을 사용하는 것 말이다.


단위(Unit) 테스트

어플리케이션을 가장 작은 단위로 분리하여 테스트하는 것을 말한다. 프로젝트를 설계하면서 util 함수, 커스텀 훅, 컴포넌트 파일 등을 생성하게 된다. 이에 대한 테스트 코드를 설계하는것이 단위 테스트이다.

컴포넌트를 테스트할 경우 자식 컴포넌트까지 포함하여 렌더링하는 Sociable Test가 있고, 자식 컴포넌트를 Mocking해서 렌더링하는 Solitary Test가 있다.

여기서 Mocking이란 모조품을 만들어 일단 코드가 돌아가게 하는것이다. 컴포넌트의 자식 컴포넌트가 있을경우 자식 컴포넌트의 모조품을 만들어 가짜로 대쳏는 것이다. 테스트하고 싶은 기능이 다른 기능들과 엮여있을 경우 유용하게 사용할 수 있다.

단위 테스트로 인해 얻을 수 있는 효과는 수행 목적에 따른 행위가 뚜렷한 함수와 컴포넌트 등을 설계하기 위해 고려하게 된다는 것이다. 또한, 이벤트도 발생시킬 수 있으며 console.log를 사용하여 일일이 디버깅 하지 않아도 테스트에서 출력이 가능히다.


Jest

jest는 단위 테스트를 위한 프레임워크이다. JavaScript로 작성된 모든 코드의 테스트를 지원하고 있으며 독립적으로 실행이 가능하다.

TypeScript에서도 @types/jest와 jest에서 typescript를 실행할 수 있게 도와주는 ts-jest만 있으면 사용할 수 있다.

과거에는 Test Runner, Test Matcher, Test Mock을 위한 라이브러리를 이것 저것 설치해서 테스트에 사용했다. 하지만 React의 Facebook팀에서 만든 jest가 등장하면서 이것으로 모두 처리가 가능해졌다.

  • 기능
    • Matcher: 예상된 값과 실제 함수 호출후 리턴값을 비교하여 성공/실패 여부를 알려준다.
    • 비동기 코드 테스팅: 비동기로 처리되는 함수의 결과값 비교가 가능하다.
    • Setup Teardown: 테스트 및 테스트 그룹의 선/후행 처리를 지원한다.
    • Mock: 위에서 설명한 모킹과 같다.
    • Snapshot 비교: 이전 버전과 같은 값을 렌더링 하는지 확인해주는 라이브러리다.

통합(Intergration) 테스트

어플리케이션의 여러 부분들이 통합되어 제대로 상호작용 되는지 테스트한다. 모듈 간의 연결에서 발생하는 에러를 검증할수 있고, 단위 테스트보다 넓은 범위에서 테스트하므로 리팩토링할 경우 사이드 이펙트가 적다.

의존성 있는 모든 모듈이 연결된 상태로 테스트하는 board test와 연결된 모듈을 모킹 또는 가상의 API로 대체하여 테스트하는 narrow test가 있다. 프론트엔드에서의 통합 테스트는 state 변화에 따른 UI변경, UI와 API간의 상호작용에 대한것이 있다. 이론 참고 자료


enzyme

Jest가 자바스크립트 테스트 프레임워크라면, enzyme은 React를 테스트하기 위해 airbnb에서 만든 라이브러리이다. enzyme는 독립적으로 사용할 수 없기 때문에 Jest와 함께 사용하여 React의 단위 테스트와 통합 테스트가 가능해진다.

  • Implementation Driven Test(구현 주도 테스트)
    enzyme는 컴포넌트의 내부(state, props, children)를 위한 테스트를 하는것에 유용하다. 실제 브라우저의 DOM이 아닌 React가 생성하는 가상 DOM을 기준으로 테스트를 작성해야한다. Sociable Test를 위한 Deep Rendering과 Solitary Test를 위한 Shallow Rendering도 제공하고 있다.

하지만 구현 주도 테스트에는 문제점이 있다. 특정 컴포넌트에 대해 통과하는 테스트를 enzyme에 구현했다고 가정하자. 유저의 DOM 조작으로 인해 state, props, tag 등이 변경될 경우 이 테스트는 실패하게 된다.


react-testing-library(RTL)

enzyme와 다르게 컴포넌트 구현 세부 사항에 중점을 두지 않고, 실제 브라우저 DOM을 기준으로 테스트를 작성한다. 그래서 RTL은 컴포넌트의 속성(state, props 등)에 대한 접근을 제공하지 않는다.

  • Behavior Driven Test(행위 주도 테스트)
    유저가 보는 화면을 기준으로 테스트 코드를 작성한다고 생각하면 편하다. 예를 들어, <title>안녕</title>에서 title을 찾아 테스트를 수행하는 것이 아니라 안녕을 찾아 테스트를 한다. DOM을 사용하여 유저가 상호작용 하는 방식을 테스트하기 때문에 DOM 조작으로 인해 내부 사항이 변경되어도 테스트가 실패하지 않는다.

또한, enzyme는 다양한 기능을 제공하기 때문에 무겁지만, RTL은 필요한 기능만 존재하기 때문에 가볍다.


E2E (End to End)

유닛 테스트로 함수를 테스트하고, 통합 테스트로 서로 다른 시스템이 잘 상하작용하는지 테스트한다. E2E 테스트는 종단(Endpoint)간 테스트로 유저의 입장에서 유저가 애플리케이션을 사용하는 상황을 가정하고 테스트를 수행한다.


cypress

E2E 테스트(단위 테스트와 통합 테스트도 가능)가 가능한 도구이다. GUI 도구를 지원하고, 스펙관리 및 디버깅이 편리하다. test runner가 jest가 아닌 mocha라는 특징이 있다.

이론 참고
RTL과 cypress 비교


selenium

cypress가 JavaScript를 사용한다면, Selenium은 여러 언어를 사용한다.

다양한 브라우저에서의 랜더링, 브라우저 별 차이를 위해 정확하고 섬세한 테스트를 위해 긴 시간을 투자할 수 있다면 Selenium을 사용할 수 있을 것이고, 프런트 엔드 개발자 본인이 자신이 작성한 코드를 빠르고 간편하게 테스트해보고 싶은 생각이 있다면 Cypress를 한번 사용해 보는 것이 나쁜 경험이 되지는 않을 것이다. selenium과 cypress


마무리

오늘은 프론트엔드에서 테스트에 대해서 알아보았다. 다음 글에서는 Jest와 요즘 많이 사용한다는 RTL(react-testing-library)로 Todo 프로젝트를 TDD로 설계해보자.

tip) TDD란 테스트 코드를 먼저 설계하고, 실제 코드를 작성하는 방법론이다.

profile
함께 일하고 싶은 개발자가 되기 위해 달려나가고 있습니다.

0개의 댓글