본문으로 건너뛰기
기술스택 · stack/javascript/테스트 자동화: 고친 코드가 망가지지 않았는지 확인하기

JavaScript · 3장 코드 품질과 도구

테스트 자동화: 고친 코드가 망가지지 않았는지 확인하기

코드를 고칠 때마다 이것저것 넣어 보며 손으로 확인하는 건 금방 지쳐요. 확인할 내용을 코드로 적어 두면, 명령 한 번으로 전부 다시 확인할 수 있어요. 테스트가 하는 일을 직접 흉내 내 보고, 실무 도구로 첫 테스트를 써 봐요.

#3.5 · Vitest 5 · Node.js 26 기준

01. 손으로 확인하기의 한계

배송비 함수를 처음 만들 때는 여러 금액을 넣어 보며 확인해요. 그런데 몇 달 뒤 "회원은 3만 원부터 무료"라는 규칙을 더하면서 함수를 고친다면, 예전에 확인했던 경우가 여전히 맞는지 다시 다 넣어 봐야 해요. 이 일을 자꾸 빼먹으니 고친 김에 다른 곳이 망가지는 버그(회귀)가 생겨요.

자동 테스트는 "이 입력이면 이 결과가 나와야 한다"를 코드로 적어 두는 거예요. 적어 둔 확인은 몇백 개든 몇 초 만에 다시 돌릴 수 있어요.

02. 테스트 도구가 하는 일, 직접 흉내 내기

테스트 도구가 하는 일은 생각보다 단순해요. 함수를 부르고, 결과가 기대한 값과 같은지 비교하고, 다르면 실패로 표시해요. 이 실행 블록에서 직접 만들어 볼게요.

// 테스트할 함수
function shippingFee(amount) {
  return amount >= 50000 ? 0 : 3000;
}

// 아주 작은 테스트 도구
function test(name, fn) {
  try {
    fn();
    console.log('✓ ' + name);
  } catch (e) {
    console.error(name + ' — ' + e.message); // 결과 칸이 ✕를 붙여요
  }
}
function expectEqual(actual, expected) {
  if (actual !== expected) throw new Error(`기대 ${expected}, 실제 ${actual}`);
}

// 테스트
test('5만 원이면 무료', () => expectEqual(shippingFee(50000), 0));
test('5만 원보다 1원 적으면 3000원', () => expectEqual(shippingFee(49999), 3000));
test('0원이어도 배송비는 3000원', () => expectEqual(shippingFee(0), 3000));

힌트대로 >=를 >로 바꿔 보면 "5만 원이면 무료"가 실패로 바뀌어요. 고친 사람이 미처 생각 못 한 경우를 테스트가 대신 기억해 둔 거예요. 간단히 확인만 하고 싶을 땐 console.assert(조건, 메시지)도 써요. 조건이 거짓일 때만 오류 줄을 남겨요.

const fee = 50000 > 50000 ? 0 : 3000;   // 일부러 틀린 계산
console.assert(fee === 0, '5만 원은 무료여야 해요');
console.assert(3000 === 3000, '이건 맞아서 아무것도 안 나와요');

03. 실무 도구로 테스트 쓰기

실무에서는 위의 test · expectEqual을 직접 만들지 않고 테스트 도구를 써요. 도구는 테스트 파일을 찾아 실행하고, 실패한 곳의 기대값과 실제값, 줄 번호를 보기 좋게 알려 줘요.

Vitest

Vite를 쓰는 프로젝트라면 Vitest가 가장 자연스러워요. 설치하고(npm), 이름에 .test.가 들어간 파일을 만들면 돼요. Vitest 5는 Node.js 22.12 이상이 필요해요.

npm install -D vitest
// shipping.js
export function shippingFee(amount) {
  return amount >= 50000 ? 0 : 3000;
}

// shipping.test.js
import { test, expect } from 'vitest';
import { shippingFee } from './shipping.js';

test('5만 원이면 무료', () => {
  expect(shippingFee(50000)).toBe(0);
});

test('5만 원보다 1원 적으면 3000원', () => {
  expect(shippingFee(49999)).toBe(3000);
});

package.json의 scripts에 "test": "vitest"를 두고 npm test로 실행해요. 기본은 지켜보기 모드라서 파일을 저장할 때마다 관련 테스트를 다시 돌려요. 한 번만 돌리려면 vitest run을 써요(자동 검사 서버에서).

Node.js 기본 테스트 러너

설치 없이 시작하고 싶다면 Node.js에 들어 있는 node:test도 있어요. 안정 기능이고, node --test를 실행하면 *.test.js 같은 이름의 파일을 찾아 돌려요.

// shipping.test.js
import test from 'node:test';
import assert from 'node:assert/strict';
import { shippingFee } from './shipping.js';

test('5만 원이면 무료', () => {
  assert.equal(shippingFee(50000), 0);
});

04. 무엇을 테스트할까

모든 값을 다 넣어 볼 수는 없어요. 버그가 잘 숨는 곳부터 골라요.

  • 경계값: 조건이 바뀌는 바로 그 값과 하나 옆의 값 (49999 · 50000). >와 >= 실수는 여기서만 드러나요.
  • 빈 값 · 0 · 이상한 값: 빈 배열, 0, 음수, undefined. 2장에서 본 || 기본값 버그가 여기서 잡혀요.
  • 버그가 났던 경우: 버그를 고칠 때 그 경우를 테스트로 먼저 적어 두면, 같은 버그가 다시 생기지 않아요.
①규칙을 바꾸려고 테스트를 먼저 더하면 그 테스트만 빨간색(실패)이에요. 함수를 고치면 모두 초록색이 되고, 그 상태를 지키면서 코드를 다듬어요.

그림처럼 실패하는 테스트를 먼저 쓰고 → 통과하게 고치고 → 정리하는 순서를 테스트 주도 개발(TDD)이라고 해요. 늘 이렇게 할 필요는 없지만, 버그를 고칠 때만큼은 이 순서가 특히 좋아요. 테스트 이름은 문장으로 써요. 실패했을 때 이름만 보고 무엇이 깨졌는지 알 수 있게요.

수량 할인 규칙은 "10개 이상 10%, 30개 이상 20%"예요. 아래 함수에 테스트를 더해서 버그를 찾아내고, 함수를 고쳐서 모든 테스트가 통과하게 해 보세요. 경계값부터 넣어 보세요.

function discountRate(qty) {
  if (qty >= 10) return 0.1;
  if (qty >= 30) return 0.2;
  return 0;
}

function test(name, fn) {
  try { fn(); console.log('✓ ' + name); }
  catch (e) { console.error(name + ' — ' + e.message); }
}
function expectEqual(actual, expected) {
  if (actual !== expected) throw new Error(`기대 ${expected}, 실제 ${actual}`);
}

test('9개는 할인 없음', () => expectEqual(discountRate(9), 0));
test('10개는 10%', () => expectEqual(discountRate(10), 0.1));
// 여기에 테스트를 더해 보세요
해답 보기

30개 경계를 확인하면 실패해요. 첫 조건 qty >= 10이 30에도 참이라 0.1을 먼저 돌려주거든요(2장 조건 순서). 큰 조건을 먼저 두면 모두 통과해요.

test('29개는 10%', () => expectEqual(discountRate(29), 0.1));
test('30개는 20%', () => expectEqual(discountRate(30), 0.2)); // 고치기 전: ✕ 기대 0.2, 실제 0.1

function discountRate(qty) {
  if (qty >= 30) return 0.2;
  if (qty >= 10) return 0.1;
  return 0;
}

05. 실전 포인트

  • 언제 쓰나요? 계산 · 변환 · 규칙처럼 입력과 결과가 분명한 함수부터 테스트해요. 테스트는 저장소에 함께 올리고, 합치기 전에 자동 검사(CI)에서 vitest run이 돌게 해요.
  • 대신 쓰는 것: 화면 전체를 클릭하며 확인하는 테스트(Playwright 같은 E2E 도구)는 느리고 깨지기 쉬워서 핵심 흐름 몇 개에만 써요. 대부분은 빠른 함수 단위 테스트로 채워요. 결과를 돌려주는 함수(2장 return)가 테스트하기 쉬워요.
  • 흔한 실수: 통과하는 경우만 테스트하는 것, 하나의 테스트에 여러 동작을 몰아넣어 무엇이 깨졌는지 모르는 것, 테스트가 실패하자 테스트 쪽을 고쳐서 통과시키는 것(기대값이 정말 틀렸는지 먼저 확인해요)이에요.

정리

  • 테스트는 "이 입력이면 이 결과"를 코드로 적어 두는 것이에요. 고친 뒤 다른 곳이 망가지는 걸 막아요.
  • Vitest는 .test. 파일의 test · expect(…).toBe(…)로, Node.js는 설치 없이 node --test로 돌려요.
  • 경계값 · 빈 값 · 버그가 났던 경우부터 테스트해요.
  • 버그를 고칠 땐 실패하는 테스트를 먼저 쓰고, 통과하게 고쳐요.
SIMPLE · 기술스택 / JavaScript — 3장 코드 품질과 도구