이 튜토리얼에서는 리액트 프로젝트에서 TDD (Test Driven Development · 테스트 주도 개발)을 하는 방법에 대해서 알아보겠습니다. 소프트웨어 개발에서의 "테스트" 라는 개념에 대해서 1도 모르는 개발자도 이 튜토리얼을 마치고나면, "나는 리액트 테스팅을 잘 할 수 있다!" 라고 말 할 수 있습니다.

이 튜토리얼을 진행하기 전에는 Node.jsyarn 이 설치되어있어야 합니다. yarn 을 사용하는것을 권장드리지만, yarn 을 싫어하신다면 npm 을 쓰셔도 상관없습니다.

테스트란?

테스트란, 우리가 작성한 코드가 잘 작동한다는 것을 검증하는 작업을 의미합니다. 특정 기능이 잘 작동하는지 확인하려면 우리는 어떻게 해야할까요? 가장 기본적인 방법으로는 우리가 구현한 기능을 직접 사용해보는 것입니다. 예를 들어서 직접 마우스로 눌러보고, 키보드로 입력해서 우리의 의도대로 잘 작동하는건지 확인하는 것이죠.
그런데 우리가 만든 프로젝트의 모든 기능을 사람이 수동으로 하나하나 확인하는 것은 정말 번거로운 일 입니다. 그래서 우리는 테스트 자동화라는 작업을 합니다. 사람이 직접 확인을 하는 것이 아니라 테스트를 하는 코드를 작성해서, 테스트 시스템이 자동으로 확인을 해줄 수 있게 하는 것이죠. 이를 테스트 자동화라고 합니다.

테스트 자동화를 통해 얻을 수 있는 이점

테스트 자동화를 함으로서 어떤 이점을 얻을 수 있을까요? 만약 여러분들이 프로젝트를 다른 사람들과 협업을 하게 되는 경우 테스트 코드를 작성하는 것은 매우 큰 도움을 줍니다. 예를들어 여러분이 코드 A 를 작성하고, 다른 개발자가 코드 B 를 작성했다고 가정해봅시다. 어느날 여러분이 코드를 수정하면서 A 코드와 B 코드를 조금씩 수정했는데, 여러분이 직접 확인 할 때는 모두 잘 작동하는지 알았는데, 알고보니 사소한 실수를 했다거나 어떠한 상황을 고려하지 못해서 B 코드의 기능의 일부가 고장나있을 수도 있습니다. 그런데 그걸 캐치하지 못하고 서비스가 배포되어 치명적인 버그가 있는 상태로 사용자에게 제공되었다면? 매우 안타까운 일이겠죠? 만약에 프로젝트의 규모가 정말 커서 고려해야 할 사항이 많거나, 여러명이 작업을 진행 한 코드여서 코드를 수정한 사람이 해당 코드를 모두 제대로 파악하고 있지 않을 때 이러한 일이 발생하기 쉽습니다. 그런데 만약 우리가 테스트 자동화를 했더라면 우리가 준비해놓은 상황에 대하여 자동으로 빠르게 검사를 해줄 수 있기 때문에 코드가 이전과 똑같이 작동하는지 아니면 고장났는지 쉽게 판단을 할 수 있어서 이러한 안타까운 일을 방지 할 수 있습니다.

테스트 코드를 사용하면 우리가 프로젝트를 개발하는 과정에서 우리가 써내려가는 코드가 기존의 기능들을 실수로 망가뜨리는것을 아주 효과적으로 방지 할 수 있습니다. 또한 개발하게 될 떄 실제 발생 할 수 있게 되는 상황에 대하여 미리 정리해놓고 그에 맞춰 코드를 작성하게 되면 우리가 실수로 빠뜨릴 수 있는 사항들을 까먹지 않고 잘 챙길 수 있게 됩니다.

이 뿐만이 아닙니다. 코드를 리팩토링 할 때 정말 좋습니다. 예를 들어서 우리가 A 라는 기능을 리팩토링한다고 가정해봅시다. 리팩토링을 하고 있는 기능이 규모가 커다란 기능이라면 실수로 빠뜨릴 수 있는 사항도 있을 수 있습니다. 그래서, 리팩토링 이후에는 버그가 있는지 없는지 정말 세밀하게 또 확인을 해봐야하죠. 그런데? 테스트 코드가 존재한다면, 리팩토링 이후에 코드가 이전과 똑같이 작동하는지 검증하는게 매우 쉬워지기 때문에 코드의 질을 향상시키는 것에 매우 큰 도움이 됩니다.

테스트 코드를 작성한다고해서 프로젝트에서 버그가 발생하지 않는 것은 아닙니다. 테스트 코드를 작성해도, 프로젝트에는 버그가 발생할 수 있습니다. 하지만, 만약 버그가 발생했더라면, 그 버그를 고치고 나서, 버그가 발생하는 상황에 대한 테스트 코드를 작성해두면, 두번 다시 똑같은 실수를 하는 것을 방지할 수 있습니다.

유닛 테스트와 통합 테스트

테스트 코드는 크게 두 종류로 나뉘어질 수 있습니다.

유닛(Unit) 테스트

첫번째는 유닛 테스트입니다. 유닛 테스트는 아주 조그마한 단위로 작성됩니다. 한번 유닛 테스트의 예시들을 확인해볼까요?

  • 컴포넌트가 잘 렌더링된다.
  • 컴포넌트의 특정 함수를 실행하면 상태가 우리가 원하는 형태로 바뀐다
  • 리덕스의 액션 생성 함수가 액션 객체를 잘 만들어낸다
  • 리덕스의 리듀서에 상태와 액션객체를 넣어서 호출하면 새로운 상태를 잘 만들어준다.

프로젝트의 기능을 잘게잘게 쪼개서 테스트를 하면 각 기능이 모두 잘 작동하는지 확인 할 수는 있습니다. 그런데, 전체적으로 잘 작동하는지 확인이 잘 안될 수도 있습니다.

통합(Integrated) 테스트

기능들이 전체적으로 잘 작동하는지 확인하기 위해서 사용 하는 것이 바로 통합 테스트입니다. 통합 테스트에 대한 예시도 확인해볼까요?

  • 여러 컴포넌트들을 렌더링하고 서로 상호 작용을 잘 하고 있다
  • DOM 이벤트를 발생 시켰을 때 우리의 UI 에 원하는 변화가 잘 발생한다
  • 리덕스와 연동된 컨테이너 컴포넌트의 DOM 에 특정 이벤트를 발생시켰을 때 우리가 원하는 액션이 잘 디스패치 된다

유닛 테스트와 통합 테스트 간의 차이는 간단합니다. 유닛 테스트는 하나에 초점을 둔다면, 통합 테스트는 이름이 그러하듯 여러 요소들을 고려하여 작성합니다. 유닛 테스트는 보통 한 파일만 불러와서 진행하는 반면, 통합 테스트는 여러 요소들을 고려하는 과정에서, 여러 파일들을 불러와서 사용하게 될 수도 있습니다. 추가적으로, 한 파일에 있는 여러 기능들을 함께 사용하는 것도 통합테스트로 간주됩니다.