320.30 c

Description: Jest와 관련된 내용과 함께 Javascript에서 테스트란 무엇인지에 대해 생각해보기

#300#Applications#320#Frontend#320.30#JavaScript#320.30 c#Jest_관련_JavaScript에서의_테스트_개요

Jest 관련 JavaScript에서의 테스트 개요

JavaScript 개발에서 테스트는 코드의 신뢰성과 유지보수성을 높이는 중요한 과정입니다. 그 중에서도 Jest는 가장 인기 있는 테스트 프레임워크 중 하나로, 다양한 환경에서 손쉽게 테스트를 작성하고 실행할 수 있도록 도와줍니다. 이번 글에서는 테스트 작성 방법 , Jest의 기본 개념과 사용 이유 등을 예제와 함께 살펴보겠습니다.

테스트를 하는 이유

테스트의 주요 목적은 코드가 의도한 대로 동작하는지 검증하는 것입니다. 이를 통해 다음과 같은 장점을 얻을 수 있습니다.

  1. 정확성 검증: 원하는 입력에 대해 예상한 결과가 나오는지 명확하게 확인할 수 있습니다.
  2. 객관성 확보: 수동 테스트와 달리 자동화된 테스트는 일관된 결과를 제공하여 객관적인 검증이 가능합니다.

예를들어 개인이 단순하게 테스트하는 경우 로컬에서 테스트하다보니 일관성이 부족하고, 파일과 같은 형태로 남지 않기에 정밀한 테스트라고 불리기에는 무리가 있다고 생각합니다.

테스트는 크게 정상 케이스 검증과 예외 케이스 검증, 그리고 프로그래밍적 검증으로 나눌 수 있습니다.

예외 케이스 검증과 프로그래밍적 검증은 다소 유사해 보일 수 있지만, 전자는 논리적 오류를 확인하는 데 중점을 두고, 후자는 기술적 문제를 해결하기 위해 수행된다고 생각합니다.

테스트 프레임워크로 Jest를 사용하는 이유

Jest는 JavaScript를 위한 가장 유명한 테스트 프레임워크 중 하나로, 이전에 여러 라이브러리를 사용하던 환경과는 달리 하나의 도구만으로 다양한 테스트 요구사항을 충족할 수 있습니다.

  1. 단일 툴의 편리성: Jest는 테스트 러너, 어서션 라이브러리, 모킹 기능 등을 하나로 통합하여 제공합니다. 별도의 설정 없이도 즉시 사용할 수 있어 개발 환경 설정이 간편합니다.
  2. 광범위한 환경 지원: Babel, TypeScript, Node.js, React, Angular, Vue.js 등 다양한 개발 환경과 호환됩니다. 이는 다양한 프로젝트에서 Jest를 손쉽게 도입할 수 있게 합니다.

공식 홈페이지: https://jestjs.io/

테스트를 들어가기 앞서

테스트를 효과적으로 작성하기 위해서는 테스트의 상황과 요구사항을 상세하게 정의하는 것이 중요합니다. 예를 들어, "사용자가 두 개의 숫자를 입력하고, 프로그램이 두 숫자의 합을 반환한다"라는 기능을 테스트한다고 가정해보겠습니다. 이때 고려해야 할 사항은 다음과 같습니다.

  1. 입력 조건:
    • 숫자가 양수인지 음수인지
    • 숫자의 범위 (예: 정수, 소수, 큰 숫자 등)
    • 입력 형식 (예: 문자열로 입력되는지, 숫자로 입력되는지)
  2. 출력 조건:
    • 반환되는 값의 형식 (문자열, 숫자 등)
    • 출력 형식의 일관성 (예: 소수점 자리수)
  3. 테스트 케이스 선정:
    • 정상 케이스: 다양한 정상 입력에 대한 테스트
    • 예외 케이스: 잘못된 입력, 경계값, 에러 발생 상황 등

이와 같이 테스트 전에 요구사항을 명확히 하고, 필요한 테스트 케이스를 선정하는 과정이 중요합니다. 이를 통해 불필요한 테스트를 줄이고, 핵심적인 부분에 집중할 수 있습니다.

Jest 구조

Jest는 테스트 파일을 관리하기 위한 여러 가지 구조를 제공합니다. 대표적인 구조는 다음과 같습니다.

운영 및 관리 측면에서 __tests__ 디렉터리에 테스트 파일을 모아 관리하는 방법을 추천합니다.

Matcher

Matcher는 Jest에서 테스트의 기대값을 정의하는 중요한 요소입니다. Matcher를 통해 입력값과 기대값을 다양한 방식으로 비교할 수 있습니다. 주요 Matcher는 다음과 같습니다.

Matcher는 상황에 맞게 적절히 사용하여 테스트의 정확성을 높일 수 있습니다.

Jest 구현 예시: 단순한 입출력

다음은 Jest를 사용한 간단한 입출력 테스트 예제입니다.

// sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;

// sum.test.js
const sum = require('./sum');

test('두 숫자의 합을 반환한다', () => {
  expect(sum(1, 2)).toBe(3);
  expect(sum(-1, 1)).toBe(0);
  expect(sum(0, 0)).toBe(0);
});

위 예제에서 toBe Matcher를 사용하여 sum 함수가 올바른 결과를 반환하는지 검증합니다. 객체의 경우 toEqual을 사용하여 구조적 동등성을 확인할 수 있습니다.
toBetoEqual의 경우 원시값(단순한 문자열이나 숫자)에서는 차이가 없지만 오브젝트와 같은 객체에서는 다른 결과값을 가지기에 주의가 필요합니다.

// user.js
function createUser(name, age) {
  return { name, age };
}
module.exports = createUser;

// user.test.js
const createUser = require('./user');

test('사용자 객체를 생성한다', () => {
  const user = createUser('Alice', 30);
  expect(user).toEqual({ name: 'Alice', age: 30 });
});

Jest 구현 예시: 비동기 패턴

비동기 함수의 테스트는 Jest에서 특별한 처리가 필요합니다. 다음은 비동기 함수를 테스트하는 예제입니다.
비동기패턴의 경우 테스트시 아무 설정도 안한다면 무조건 성공하게됩니다. 왜냐하면 테스트의 경우 문제없이 지나갔기 때문에 시간이 흐른뒤에 오는 결과값을 신경쓰지 않기 때문이죠. 그래서 비동기 함수의 테스트는 async/await를 사용하거나 done 콜백을 사용하여 테스트가 완료될 시점을 명시해야 합니다.

// fetchData.js
const fetchData = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('데이터 반환');
    }, 1000);
  });
};
module.exports = fetchData;

// fetchData.test.js
const fetchData = require('./fetchData');

test('비동기 데이터 반환', async () => {
  await expect(fetchData()).resolves.toBe('데이터 반환');
});

Jest 구현 예시: Mock 함수를 통한 임의의 함수값 구현

모킹은 특정 함수의 동작을 가짜로 구현하여 테스트의 독립성을 높이는 데 유용합니다. 우리가 테스트하는 함수가 항상 제어권이 있는 것은 아닙니다. 외부의 API라던가 DB 에접속해야하는 경우 또는 아직 만들어지지 않았지만 결과값은 알고있는 경우와 같은 케이스에 mock을 사용하게 됩니다.

// fetchUser.js
const fetchUser = (callback) => {
  setTimeout(() => {
    callback({ name: 'Alice' });
  }, 1000);
};
module.exports = fetchUser;

// fetchUser.test.js
const fetchUser = require('./fetchUser');

test('사용자 데이터를 가져온다', done => {
  const mockCallback = jest.fn(user => {
    expect(user).toEqual({ name: 'Alice' });
    expect(mockCallback).toHaveBeenCalledTimes(1);
    done();
  });
  fetchUser(mockCallback);
});

jest.fn()을 사용하여 모의 함수를 생성하고, 호출 횟수나 전달된 인자를 검증할 수 있습니다.

Jest 구현 예시: Snapshot을 통한 렌더링된 페이지 비교

스냅샷 테스트는 UI 컴포넌트가 의도한 대로 렌더링되는지 확인하는 데 유용합니다. 해당 테스트의 경우 렌더링된 결과를 저장하고 renderscreen을 통해 값을 비교할 수 있습니다.

// MyComponent.test.jsx
import React from 'react';
import { render, screen } from '@testing-library/react';
import '@testing-library/jest-dom'; // 추가적인 매처를 사용하기 위해 필요
import MyComponent from './MyComponent';

test('MyComponent 스냅샷 테스트', () => {
  const { asFragment } = render(<MyComponent />);
  expect(asFragment()).toMatchSnapshot();
});

test('MyComponent 렌더링 확인', () => {
  render(<MyComponent />);
  
  // 제목이 올바르게 렌더링되었는지 확인
  const heading = screen.getByText('안녕하세요!');
  expect(heading).toBeInTheDocument();
  
  // 문단이 올바르게 렌더링되었는지 확인
  const paragraph = screen.getByText('이것은 스냅샷 테스트 예제입니다.');
  expect(paragraph).toBeInTheDocument();
});

스냅샷 테스트를 통해 컴포넌트의 렌더링 결과가 변경되었는지 쉽게 확인할 수 있습니다.

Jest를 사용해야 하는 경우와 그렇지 않은 경우를 구분해야 하는 이유

Jest는 강력한 기능을 제공하지만, 모든 상황에서 최적의 선택은 아닐 수 있습니다. 다음과 같은 경우 Jest 사용을 고려해야 합니다. 왜냐하면 빈번히 기준이 바뀌는 경우의 테스트라던가, 테스트 작성으로 오히려 개발프로세스에 지장을 준다던가 하는 경우가 발생할 수 있기 때문입니다.

다음은 보통 사용하는 사례

적절한 도구 선택은 프로젝트의 요구사항과 특성에 따라 달라지므로, 상황에 맞게 판단하는 것이 중요합니다.

참고 자료