프로그래밍 숲

우테코 6기 | test 코드 (jest) 분석하기 본문

프로그래밍_인포/Javascript

우테코 6기 | test 코드 (jest) 분석하기

jjscript 2023. 10. 31. 19:42
728x90
반응형

우테코 6기 2차 과제인 자동차 경주에서 기본으로 주어진 ApplicationTest.js 파일을 분석해보려고 한다.

테스트코드 폴더 구조와 파일명

우선 test 코드는 .test.js로 끝나야한다고 알고 있었는데, 주어진 파일명은 ApplicationTest.js였다. 찾아보니 이유는 다음과 같았다.


Jest는 기본적으로 특정 패턴을 따르는 파일을 테스트 파일로 인식합니다. 기본 설정에 따르면, Jest는 다음과 같은 파일을 테스트 파일로 인식합니다:

  • __tests__ 폴더 내의 모든 .js 파일
  • .test.js로 끝나는 모든 파일
  • .spec.js로 끝나는 모든 파일

따라서 __tests__/ApplicationTest.js 파일은 __tests__ 폴더 내에 위치해 있기 때문에 Jest에 의해 테스트 파일로 인식됩니다.

그러나 테스트 파일의 네이밍 컨벤션을 일관되게 유지하는 것이 좋습니다. 이는 프로젝트의 가독성과 유지보수성을 높이는 데 도움이 됩니다. 일반적으로 .test.js 또는 .spec.js 확장자를 사용하는 것이 좋으며, 이는 Jest의 기본 설정과도 일치합니다.

만약 다른 패턴을 사용하고 싶다면, Jest 설정에서 testMatch 옵션을 사용하여 테스트 파일을 인식하는 패턴을 커스터마이즈할 수 있습니다. 예를 들어, jest.config.js 파일에서 다음과 같이 설정할 수 있습니다:

module.exports = {
  // ...
  testMatch: [
    "**/__tests__/**/*.[jt]s?(x)",
    "**/?(*.)+(spec|test).[jt]s?(x)"
  ]
};

ApplicationTest.js 분석

1. 모듈 임포트 및 테스트 설정

import { MissionUtils } from '@woowacourse/mission-utils';
import App from '../src/App.js';

@woowacourse/mission-utils 라이브러리와 App 클래스를 임포트합니다. MissionUtils는 입력, 출력, 무작위 숫자 생성 등의 유틸리티 함수를 제공하며, App 클래스는 자동차 경주 게임의 메인 로직을 담고 있습니다.

2. 입력 및 랜덤 숫자 모의 함수

물론이지요! 아래 코드들은 Jest를 사용하여 모의 함수(Mock Functions)를 구현하는 부분입니다. 이러한 모의 함수들은 테스트 중에 외부 모듈의 함수 호출을 제어하고 추적할 수 있게 해줍니다.

1. mockQuestions 함수

const mockQuestions = inputs => {
  MissionUtils.Console.readLineAsync = jest.fn();

  MissionUtils.Console.readLineAsync.mockImplementation(() => {
    const input = inputs.shift();
    return Promise.resolve(input);
  });
};

이 함수는 사용자의 입력을 모의하는 함수입니다. Console.readLineAsync 함수를 Jest의 모의 함수(jest.fn())로 대체합니다. 그리고 mockImplementation 함수를 사용하여 이 모의 함수가 호출될 때 실행할 로직을 정의합니다.

  • inputs: 사용자 입력을 배열로 받아서, 각 테스트 케이스에서 사용할 입력 값을 순서대로 제공합니다.
  • inputs.shift(): 입력 배열에서 첫 번째 값을 제거하고 반환합니다. 이렇게 하면 각 호출마다 다음 입력 값을 사용할 수 있습니다.
  • Promise.resolve(input): Console.readLineAsync 함수는 비동기 함수이므로, Promise를 사용하여 비동기적으로 값을 반환합니다.

2. mockRandoms 함수

const mockRandoms = numbers => {
  MissionUtils.Random.pickNumberInRange = jest.fn();
  numbers.reduce((acc, number) => {
    return acc.mockReturnValueOnce(number);
  }, MissionUtils.Random.pickNumberInRange);
};

이 함수는 무작위 숫자 생성을 모의하는 함수입니다. Random.pickNumberInRange 함수를 모의 함수로 대체합니다. mockReturnValueOnce 함수를 사용하여 이 모의 함수가 호출될 때마다 반환할 값을 순서대로 정의합니다.

  • numbers: 무작위로 생성되어야 하는 숫자들을 배열로 받습니다.
  • numbers.reduce(...): 배열의 각 숫자에 대해 mockReturnValueOnce를 호출하여 순서대로 반환할 값을 설정합니다.

3. getLogSpy 함수

const getLogSpy = () => {
  const logSpy = jest.spyOn(MissionUtils.Console, 'print');
  logSpy.mockClear();
  return logSpy;
};

이 함수는 Console.print 함수의 호출을 감시하는 스파이(Spy)를 생성합니다.

  • jest.spyOn(...): 주어진 객체의 메서드를 감시합니다. 이 경우 MissionUtils.Console.print 함수를 감시합니다.
  • logSpy.mockClear(): 이전에 호출된 로그를 지웁니다. 이렇게 하면 각 테스트가 서로 독립적으로 실행될 수 있습니다.
  • 함수는 logSpy를 반환하여, 테스트 케이스에서 이 스파이를 사용하여 Console.print 함수의 호출을 검증할 수 있게 합니다.

이러한 모의 함수와 스파이는 테스트 중에 코드의 동작을 제어하고 검증하는 데 필수적인 도구입니다. 이를 통해 외부 의존성을 제거하고 예측 가능한 환경에서 코드를 테스트할 수 있습니다.

4. 자동차 경주 게임 테스트

describe('자동차 경주 게임', () => {
  // 테스트 케이스들
});

describe 함수는 여러 관련된 테스트 케이스를 그룹화합니다. 이 경우에는 "자동차 경주 게임"에 대한 테스트들을 그룹화하고 있습니다.

5. 개별 테스트 케이스

test('전진-정지', async () => { /* ... */ });
test.each([[['pobi,javaji']], [['pobi,eastjun']]])('이름에 대한 예외 처리', async inputs => { /* ... */ });
test('유효하지 않은 시도 횟수 입력', async () => { /* ... */ });
// ... (그 외의 테스트 케이스들)

개별 테스트 케이스들입니다. 각 테스트 케이스는 test 함수를 사용하여 정의되며, 비동기 함수를 사용하여 비동기 로직을 테스트합니다.

  • 전진-정지: 자동차 한 대가 전진하고 정지하는 상황을 테스트합니다.
  • 이름에 대한 예외 처리: 자동차 이름이 유효하지 않은 경우 예외가 발생하는지 테스트합니다.
  • 유효하지 않은 시도 횟수 입력: 유효하지 않은 시도 횟수를 입력한 경우 예외가 발생하는지 테스트합니다.
  • ...: 그 외에도 다양한 상황을 테스트합니다.

각 테스트 케이스 안에서는 given-when-then 패턴을 따라 테스트를 구성합니다.

  • given: 테스트를 위한 준비 단계입니다. 입력값을 설정하고 모의 함수를 구성합니다.
  • when: 실제로 테스트하고자 하는 동작을 실행합니다.
  • then: 결과를 검증합니다. 예상한 출력이나 상태가 실제와 일치하는지 확인합니다.

이렇게 구성된 테스트 케이스들은 자동차 경주 게임의 다양한 기능과 상황을 검증하여 버그를 예방하고 코드의 안정성을 높이는 데 도움을 줍니다.

728x90
반응형
Comments