테스트와 TypeScript — Vitest, Jest에서 타입 활용하기
TypeScript와 테스트 프레임워크를 함께 쓰면 테스트 코드 자체도 타입 안전 해지고, mock 객체나 fixture도 타입 검사를 받습니다.
Vitest 설정
npm install --save-dev vitest
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
globals: true, // describe, it, expect를 import 없이 사용
},
});
Vitest는 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}`;
}
// 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
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
// 모듈 전체를 mock
vi.mock('./database', () => ({
getUser: vi.fn().mockResolvedValue({ id: 1, name: '홍길동' }),
saveUser: vi.fn().mockResolvedValue(true),
}));
타입 레벨 테스트
타입이 올바르게 동작하는지 타입 레벨에서 검증 하는 방법입니다.
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 설정
npm install --save-dev jest ts-jest @types/jest
// jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
};
// Jest 테스트
describe('add', () => {
it('두 숫자를 더한다', () => {
expect(add(1, 2)).toBe(3);
});
});
Jest의 타입 헬퍼
// jest.Mocked로 모듈의 모든 함수를 mock 타입으로
import type { jest } from '@jest/globals';
const mockedModule = jest.mocked(originalModule);
// 모든 함수가 jest.Mock 타입으로 변환됨
테스트 데이터 팩토리
// 테스트 데이터를 타입 안전하게 생성
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(); // 기본값 사용
비동기 테스트
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)- 팩토리 함수로 타입 안전한 테스트 데이터를 생성하자
댓글 로딩 중...