TypeScript와 테스트 프레임워크를 함께 쓰면 테스트 코드 자체도 타입 안전 해지고, mock 객체나 fixture도 타입 검사를 받습니다.

Vitest 설정

BASH
npm install --save-dev vitest
TYPESCRIPT
// vitest.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true, // describe, it, expect를 import 없이 사용
  },
});

Vitest는 TypeScript를 별도 설정 없이 바로 지원합니다.

기본 테스트 작성

TYPESCRIPT
// utils.ts
export function add(a: number, b: number): number {
  return a + b;
}

export function formatName(first: string, last: string): string {
  return `${last} ${first}`;
}
TYPESCRIPT
// utils.test.ts
import { describe, it, expect } from 'vitest';
import { add, formatName } from './utils';

describe('add', () => {
  it('두 숫자를 더한다', () => {
    expect(add(1, 2)).toBe(3);
    // add('1', 2); // ❌ 컴파일 에러 — 테스트 코드에서도 타입 검사
  });
});

describe('formatName', () => {
  it('성 이름 순서로 포맷한다', () => {
    expect(formatName('길동', '홍')).toBe('홍 길동');
  });
});

Mock 타이핑

함수 Mock

TYPESCRIPT
import { vi, describe, it, expect } from 'vitest';

// 타입이 지정된 mock 함수
const mockFetch = vi.fn<[string], Promise<{ data: string }>>()
  .mockResolvedValue({ data: '결과' });

// 또는 기존 함수를 mock
import * as api from './api';

vi.spyOn(api, 'fetchUser').mockResolvedValue({
  id: 1,
  name: '홍길동',
  email: 'test@test.com',
});

모듈 Mock

TYPESCRIPT
// 모듈 전체를 mock
vi.mock('./database', () => ({
  getUser: vi.fn().mockResolvedValue({ id: 1, name: '홍길동' }),
  saveUser: vi.fn().mockResolvedValue(true),
}));

타입 레벨 테스트

타입이 올바르게 동작하는지 타입 레벨에서 검증 하는 방법입니다.

TYPESCRIPT
import { expectTypeOf } from 'vitest';

describe('타입 테스트', () => {
  it('add의 반환 타입은 number이다', () => {
    expectTypeOf(add).returns.toBeNumber();
  });

  it('add의 매개변수는 number이다', () => {
    expectTypeOf(add).parameter(0).toBeNumber();
    expectTypeOf(add).parameter(1).toBeNumber();
  });

  it('User 타입이 올바르다', () => {
    expectTypeOf<User>().toHaveProperty('name');
    expectTypeOf<User>().toHaveProperty('age');
  });
});

expectTypeOf는 Vitest에 내장되어 있으며, 런타임에서는 아무것도 하지 않고 컴파일 타임에만 검사합니다.

Jest 설정

BASH
npm install --save-dev jest ts-jest @types/jest
JAVASCRIPT
// jest.config.js
module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
};
TYPESCRIPT
// Jest 테스트
describe('add', () => {
  it('두 숫자를 더한다', () => {
    expect(add(1, 2)).toBe(3);
  });
});

Jest의 타입 헬퍼

TYPESCRIPT
// jest.Mocked로 모듈의 모든 함수를 mock 타입으로
import type { jest } from '@jest/globals';

const mockedModule = jest.mocked(originalModule);
// 모든 함수가 jest.Mock 타입으로 변환됨

테스트 데이터 팩토리

TYPESCRIPT
// 테스트 데이터를 타입 안전하게 생성
interface User {
  id: number;
  name: string;
  email: string;
  role: 'admin' | 'user';
  createdAt: Date;
}

// 기본값이 있는 팩토리 함수
function createTestUser(overrides: Partial<User> = {}): User {
  return {
    id: 1,
    name: '홍길동',
    email: 'hong@test.com',
    role: 'user',
    createdAt: new Date('2026-01-01'),
    ...overrides,
  };
}

// 사용
const admin = createTestUser({ role: 'admin', name: '관리자' });
const user = createTestUser(); // 기본값 사용

비동기 테스트

TYPESCRIPT
import { describe, it, expect, vi, beforeEach } from 'vitest';

// 비동기 함수 테스트
describe('UserService', () => {
  const mockDb = {
    findUser: vi.fn<[number], Promise<User | null>>(),
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('사용자를 찾으면 반환한다', async () => {
    const testUser = createTestUser({ id: 1 });
    mockDb.findUser.mockResolvedValue(testUser);

    const result = await getUser(1);
    expect(result).toEqual(testUser);
    expect(mockDb.findUser).toHaveBeenCalledWith(1);
  });

  it('사용자가 없으면 null을 반환한다', async () => {
    mockDb.findUser.mockResolvedValue(null);

    const result = await getUser(999);
    expect(result).toBeNull();
  });
});

정리

  • Vitest는 TypeScript를 별도 설정 없이 바로 지원한다
  • Jest는 ts-jest 프리셋으로 TypeScript를 지원한다
  • mock 함수와 모듈도 타입 검사를 받으므로 잘못된 mock을 방지한다
  • expectTypeOf로 타입 레벨 테스트를 할 수 있다 (Vitest)
  • 팩토리 함수로 타입 안전한 테스트 데이터를 생성하자
댓글 로딩 중...