관리 메뉴

즐겁게, 코드

프론트엔드 테스팅 첫걸음 : jest 사용하기 본문

🎨 프론트엔드/Testing

프론트엔드 테스팅 첫걸음 : jest 사용하기

Chamming2 2021. 8. 19. 10:06

테스팅의 중요성

지금까지 프로젝트를 혼자 진행하면서는 printconsole.log() 등의 출력을 통해 비교적 단순한 '테스트' 를 진행할 수 있었지만, 어플리케이션의 규모가 커지고 동일한 테스트를 반복해야 하는 상황에서는 테스트 코드를 작성하는 것이 필수적입니다.

 

이번에는 자바스크립트의 강력한 테스팅 라이브러리 jest 와 리액트 프로젝트용 테스팅 라이브러리 react-testing-library(RTL) 을 통해 기초적인 테스트 방법에 대해 알아보겠습니다.

jest 설치하기

리액트 테스팅을 경험하기 전에 일반 자바스크립트 환경에서 jest 를 알아보도록 하겠습니다.
이를 위해 npm init -y 로 프로젝트를 초기화하고, jest@babel/preset-env 를 개발 의존성 파일로 설치합니다.

npm init -y
npm i -D jest @babel/preset-env

잠깐 @babel/preset-env 에 대한 설명을 덧붙이자면 테스트는 node.js 환경에서 이루어지는데요, 따라서 기본적으로는 테스트 코드에서 import 등의 ES6 문법을 사용할 수 없습니다.

 

이를 해결하기 위해 @babel/preset-env 트랜스파일링 프리셋을 설치하고, 관련 바벨 설정을 먼저 해줘야 합니다.

 

[루트 경로에 파일 생성 - babel.config.js]

// babel.config.js 파일은 프로젝트 루트 경로에 위치시킵니다.
// .babelrc와 헷갈리시면 안됩니다!!

module.exports = function (api) {
  api.cache(true);
  return {
    presets: ["@babel/preset-env"],
  };
};
****

이제 jest 를 사용할 준비가 끝났습니다!

테스트 파일 작성하기

테스팅 파일은 파일명.test.js 또는 파일명.spec.js 처럼 중간에 test 라는 이름이 하나 더 붙습니다.
(예를 들어, addNumber.js 를 테스트하는 테스트 코드의 파일명은 addNumber.test.js 가 됩니다!)

 

jest 는 기본적으로 __test__ 라는 폴더에 존재하는 파일이나, .test.js 또는 .spec.js 로 끝나는 모든 파일을 테스트 파일로 인식하고 실행합니다.

첫 번째 테스트 코드

그럼 첫 번째 테스트 코드를 작성할 간단한 함수를 만들어 보겠습니다.
addNumber.js 라는 파일에 두 수의 합을 구하는 함수를 정의한 뒤, 테스트 코드 내에서 이를 사용할 수 있도록 export 로 내보내 줍니다.

// addNumber.js
export const addNumber = (a, b) => {
  return a + b;
};

다음은 테스트 코드에서 이 함수가 제대로 동작하는지 확인할 차례입니다.
addNumber.test.js 라는 파일을 만들고, 다음과 같이 test() 함수를 작성해 봅니다.

// addNumber.test.js
test("addNumber는 두 수를 더하는 함수입니다.", () => {

})

테스트 실행하기

jest 커맨드만으로도 모든 테스트 코드를 실행할 수 있지만, 두 가지 유용한 플래그가 있습니다.

  • --coverage : 프로젝트의 테스트 커버리지를 함께 나타내줍니다.
  • --watch : 대상 코드나 테스트 코드에 변경이 생기면 테스트를 다시 실행합니다. (nodemon을 생각하시면 됩니다!)

이제, jest --watch --coverage 로 테스트를 실행해 보겠습니다.

첫 번째 테스트가 실행된 모습인데요, jest 는 테스트 코드 내에서 오류가 발생할 때만 테스트를 fail 시키기 때문에 테스트 코드를 작성하지 않아도 테스트를 통과할 수 있었습니다.


잠깐 다른 이야기

'테스트 주도 개발(Test Driven Development)' 이라는 테스트를 토대로 기능을 구현해나가는 유명한 개발 방법론이 있습니다.

"실패-성공-리팩토링 의 반복" 으로 표현되는 이 방법론은 다음처럼 적용할 수 있습니다.

 

1. 완벽한 테스트 케이스를 먼저 설계한다.

2. 제작할 함수의 기능을 해당 테스트케이스에 맞춘다.

3. 로직을 개선하면서 1 ~ 3을 반복한다.

 

어디까지나 제 생각이지만, 저는 개인적으로 좋은 개발 방법이라고 생각해 실제 함수보다 테스트 코드를 먼저 작성하는 습관을 가지면 좋을 것 같다고 추천드립니다!

TDD에 대한 설명은 벨로퍼트님 블로그에 쉽고 자세히 설명되어 있습니다!


다시 돌아와서 - 테스트 작성하기

이제 테스트 코드를 작성해 보겠습니다.
테스트 코드는 test 함수의 두 번째 인자로 주어지는 함수에 작성합니다.

import { addNumber } from "./addNumber.js";

test("addNumber는 두 수를 더하는 함수입니다.", () => {

})

테스트는 "A 조건에서 B를 실행했을 때 예상했던 결과 C가 나오는가?" 의 흐름을 따라가는 것이 권장되는데요, (이를 Arrange - Act - Assert(AAA) 패턴이라고 합니다.) 한번 이 흐름대로 코드를 작성해 보겠습니다.

 

먼저 조건을 정의해 보겠습니다. (Arrange)

  • 테스트 조건 : addNumber(a, b) 에 2와 3을 전달하면, 결과물은 5가 될 것이다.
import { addNumber } from "./addNumber.js";

test("addNumber adds two number", () => {
    // 조건
    const numberA = 2;
    const numberB = 3;
    const result = 5;
})

다음은 코드를 실행합니다. (Act)

  • 실행 : addNumber(a, b) 함수에 2와 3을 전달한다.
import { addNumber } from "./addNumber.js";

test("addNumber는 두 수를 더하는 함수입니다.", () => {
    // 조건
    const numberA = 2;
    const numberB = 3;
    const result = 5;

    // 실행
    const actResult = addNumber(numberA, numberB);
})

마지막으로, 실행 단계에서 실행한 코드를 평가합니다. (Assert)

import { addNumber } from "./addNumber.js";

test("addNumber는 두 수를 더하는 함수입니다.", () => {
    // 조건
    const numberA = 2;
    const numberB = 3;
    const result = 5;

    // 실행
    const actResult = addNumber(numberA, numberB);

    // 평가
    expect(actResult).toBe(result);
})

두 수를 구하는 addNumber 함수가 주어진 테스트를 통과한 모습입니다!

expect(actResult).toBe(result);

그런데, 실행 단계에서는 뭔가 신기한 문법이 사용된 것 같습니다.

matcher 함수

// = actResult 값이 result와 같을 것이다.
expect(actResult).toBe(result);

테스트를 평가하는 단계에서는 expect() 함수와 matcher 함수가 필요합니다. (절대 어렵지 않습니다!)
expect() 함수에는 평가할 값을 인자로 전달하고 해당 값이 특정 조건을 만족하는지를 matcher 함수로 검사하는 것입니다.

주요 matcher 함수

당연하지만 모든 matcher를 외우는 것은 비효율적인 일이고, 아래의 matcher들로도 대부분의 평가가 가능할 것이라 생각됩니다.
(이보다 복잡한 matcher가 필요하다면 공식 문서에서 확인하는 것을 추천드립니다!)

  • toBeDefined() 함수 : 값이 undefined 가 아닌지 검사한다. (주로 가장 먼저 검사한다!)
  • toBe() 함수 : 원시형 데이터를 비교한다.
  • toEqual() 함수 : 참조형 데이터를 깊은 비교를 통해 비교한다.
  • toBeTruthy() 함수 : 값이 참인지를 검사한다.
  • .toContain() 함수 : 특정 요소가 배열 안에 존재하는지를 검사한다.
  • not : 다른 매쳐 함수 앞에 체이닝해 사용하며, 해당 값의 반대값이 참인지를 검사한다.
    • 주의 : not 은 함수가 아닌 프로퍼티입니다!
반응형
Comments
소소한 팁 : 광고를 눌러주시면, 제가 뮤지컬을 마음껏 보러다닐 수 있어요!
와!! 바로 눌러야겠네요! 😆