단위 테스트: Jest와 타입스크립트 통합 🧪🚀
안녕하세요, 여러분! 오늘은 정말 핫한 주제로 찾아왔어요. 바로 Jest와 타입스크립트를 이용한 단위 테스트에 대해 깊이 파헤쳐볼 거예요. 이 주제, 어렵게 들리시나요? 걱정 마세요! 제가 쉽고 재미있게 설명해드릴게요. 마치 카톡으로 수다 떠는 것처럼요. ㅋㅋㅋ
먼저, 단위 테스트가 뭔지 아시나요? 간단히 말해서 코드의 작은 부분을 테스트하는 거예요. 마치 요리할 때 각 재료의 맛을 보는 것과 비슷하죠. 그리고 Jest는 이런 테스트를 도와주는 멋진 도구예요. 타입스크립트와 함께 쓰면 더욱 강력해진답니다! 🦸♂️
이 글을 통해 여러분은 Jest와 타입스크립트를 이용한 단위 테스트의 세계로 빠져들게 될 거예요. 마치 재능넷에서 새로운 재능을 발견하는 것처럼 말이죠! 그럼 이제 본격적으로 시작해볼까요? 🚀
1. Jest와 타입스크립트: 완벽한 콤비 💑
Jest와 타입스크립트, 이 둘의 조합이 왜 그렇게 좋은 걸까요? 마치 치즈와 와인처럼 완벽한 궁합을 자랑하거든요! 😋
Jest: Facebook에서 만든 JavaScript 테스팅 프레임워크예요. 간단하고 빠르며, 설정이 거의 필요 없어요.
타입스크립트: JavaScript에 타입을 추가한 언어로, 코드의 안정성과 가독성을 높여줘요.
이 둘을 합치면 어떻게 될까요? 바로 타입 안정성과 테스트의 신뢰성을 동시에 얻을 수 있어요! 마치 재능넷에서 여러 재능을 한 번에 배우는 것처럼 효율적이죠. 👍
Jest와 타입스크립트의 조합은 다음과 같은 이점을 제공해요:
- 코드 오류를 미리 잡아낼 수 있어요 (타입스크립트의 장점)
- 테스트 코드 작성이 더 쉬워져요 (Jest의 장점)
- 자동 완성 기능으로 개발 속도가 빨라져요
- 리팩토링이 더 안전해져요
이제 이 멋진 콤비를 어떻게 설정하고 사용하는지 자세히 알아볼까요? 🤓
2. Jest와 타입스크립트 설정하기 🛠️
자, 이제 본격적으로 Jest와 타입스크립트를 설정해볼 거예요. 걱정 마세요, 어렵지 않아요! 마치 레고 블록 조립하듯 차근차근 해볼게요. 😊
2.1 필요한 패키지 설치하기
먼저, 필요한 패키지들을 설치해야 해요. 터미널을 열고 다음 명령어를 입력해주세요:
npm install --save-dev jest typescript ts-jest @types/jest
이 명령어로 설치되는 패키지들은 다음과 같아요:
- jest: 우리의 주인공! 테스트 러너예요.
- typescript: 타입스크립트 컴파일러예요.
- ts-jest: Jest가 타입스크립트를 이해할 수 있게 해주는 도구예요.
- @types/jest: Jest의 타입 정의를 제공해요.
2.2 Jest 설정하기
이제 Jest 설정 파일을 만들어볼 거예요. 프로젝트 루트 디렉토리에 jest.config.js
파일을 만들고 다음 내용을 입력해주세요:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.ts?(x)', '**/?(*.)+(spec|test).ts?(x)'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
};
이 설정은 Jest에게 "야, 우리 타입스크립트 쓸 거야!"라고 말해주는 거예요. ㅋㅋㅋ
2.3 tsconfig.json 설정하기
타입스크립트 설정 파일도 필요해요. tsconfig.json
파일을 만들고 다음 내용을 입력해주세요:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
}
}
이 설정은 타입스크립트에게 "이렇게 동작해줘!"라고 지시하는 거예요. 마치 재능넷에서 선생님께 수업 방식을 요청하는 것처럼요! 😄
2.4 package.json 스크립트 추가하기
마지막으로, package.json
파일에 테스트 스크립트를 추가해줄게요:
"scripts": {
"test": "jest",
"test:watch": "jest --watch"
}
이제 npm test
명령어로 테스트를 실행할 수 있어요. npm run test:watch
는 파일이 변경될 때마다 자동으로 테스트를 실행해줘요. 편리하죠? 👍
여기까지 하면 기본 설정은 끝이에요! 이제 본격적으로 테스트를 작성해볼 준비가 됐어요. 다음 섹션에서 실제 테스트 코드를 작성해볼 거예요. 기대되지 않나요? 😆
3. 첫 번째 Jest 테스트 작성하기 ✍️
드디어 우리의 첫 테스트를 작성할 시간이에요! 😃 테스트 작성은 마치 퍼즐을 맞추는 것과 같아요. 재미있고 도전적이죠. 자, 시작해볼까요?
3.1 간단한 함수 만들기
먼저, 테스트할 간단한 함수를 만들어볼게요. src/math.ts
파일을 만들고 다음 코드를 입력해주세요:
export function add(a: number, b: number): number {
return a + b;
}
이 함수는 정말 간단해요. 두 숫자를 더하는 함수죠. 하지만 이 작은 함수로 우리는 많은 것을 배울 수 있어요!
3.2 테스트 파일 만들기
이제 이 함수를 테스트할 파일을 만들어볼게요. src/__tests__/math.test.ts
파일을 만들고 다음 코드를 입력해주세요:
import { add } from '../math';
describe('add function', () => {
it('should add two numbers correctly', () => {
expect(add(1, 2)).toBe(3);
expect(add(-1, 1)).toBe(0);
expect(add(5, 5)).toBe(10);
});
});
이 코드가 바로 우리의 첫 번째 Jest 테스트예요! 😎 각 부분을 자세히 살펴볼까요?
describe
: 테스트 그룹을 정의해요. 여기서는 'add function'이라는 그룹을 만들었어요.it
: 개별 테스트 케이스를 정의해요. 여기서는 "두 숫자를 올바르게 더해야 한다"는 테스트를 만들었어요.expect
: 실제 테스트를 수행해요.add
함수의 결과가 예상한 값과 일치하는지 확인해요.
3.3 테스트 실행하기
자, 이제 테스트를 실행해볼 차례예요! 터미널에서 다음 명령어를 입력해주세요:
npm test
그러면 다음과 같은 결과가 나올 거예요:
PASS src/__tests__/math.test.ts add function ✓ should add two numbers correctly (3 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 1.5 s Ran all test suites.
와우! 🎉 우리의 첫 테스트가 통과했어요! 이게 바로 단위 테스트의 매력이에요. 코드가 예상대로 동작하는지 빠르게 확인할 수 있죠.
3.4 더 복잡한 테스트 케이스 추가하기
이제 조금 더 복잡한 테스트 케이스를 추가해볼까요? math.ts
파일에 새로운 함수를 추가해볼게요:
export function multiply(a: number, b: number): number {
return a * b;
}
export function divide(a: number, b: number): number {
if (b === 0) {
throw new Error("Cannot divide by zero");
}
return a / b;
}
그리고 math.test.ts
파일에 이 함수들에 대한 테스트를 추가해볼게요:
import { add, multiply, divide } from '../math';
describe('math functions', () => {
describe('add function', () => {
it('should add two numbers correctly', () => {
expect(add(1, 2)).toBe(3);
expect(add(-1, 1)).toBe(0);
expect(add(5, 5)).toBe(10);
});
});
describe('multiply function', () => {
it('should multiply two numbers correctly', () => {
expect(multiply(2, 3)).toBe(6);
expect(multiply(-2, 3)).toBe(-6);
expect(multiply(0, 5)).toBe(0);
});
});
describe('divide function', () => {
it('should divide two numbers correctly', () => {
expect(divide(6, 2)).toBe(3);
expect(divide(-6, 2)).toBe(-3);
expect(divide(0, 5)).toBe(0);
});
it('should throw an error when dividing by zero', () => {
expect(() => divide(5, 0)).toThrow("Cannot divide by zero");
});
});
});
이제 이 테스트를 실행하면 더 다양한 케이스를 테스트할 수 있어요. 특히 divide
함수의 경우, 0으로 나누려고 할 때 에러를 던지는지도 테스트하고 있죠. 이런 식으로 예외 상황도 테스트할 수 있어요!
이렇게 테스트를 작성하면, 나중에 코드를 수정하더라도 모든 기능이 제대로 동작하는지 빠르게 확인할 수 있어요. 마치 재능넷에서 새로운 재능을 배우고 나서 실력 체크를 하는 것처럼요! 😄
다음 섹션에서는 더 복잡한 상황에서의 테스트 작성법에 대해 알아볼 거예요. 기대되지 않나요? 🚀
4. 비동기 코드 테스트하기 ⏳
자, 이제 좀 더 현실적인 상황을 다뤄볼 시간이에요! 실제 프로젝트에서는 비동기 코드를 자주 사용하게 되죠. API 호출이나 데이터베이스 쿼리 같은 것들 말이에요. 이런 비동기 코드는 어떻게 테스트할 수 있을까요? 🤔
4.1 Promise를 반환하는 함수 테스트하기
먼저, Promise를 반환하는 간단한 함수를 만들어볼게요. src/async.ts
파일을 만들고 다음 코드를 입력해주세요:
export function fetchData(): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve('데이터 가져오기 성공!');
}, 1000);
});
}
</string>
이 함수는 1초 후에 "데이터 가져오기 성공!" 메시지를 반환해요. 실제로는 API 호출이나 데이터베이스 쿼리를 시뮬레이션하는 거죠.
이제 이 함수를 테스트해볼까요? src/__tests__/async.test.ts
파일을 만들고 다음 코드를 입력해주세요:
import { fetchData } from '../async';
describe('fetchData function', () => {
it('should return data after 1 second', async () => {
const data = await fetchData();
expect(data).toBe('데이터 가져오기 성공!');
});
});
여기서 주목할 점은 async
와 await
키워드예요. 이 키워드들을 사용하면 비동기 코드를 마치 동기 코드처럼 테스트할 수 있어요. 편리하죠? 😎
4.2 콜백 함수 테스트하기
때로는 콜백 스타일의 비동기 함수를 테스트해야 할 수도 있어요. 이런 경우에는 어떻게 해야 할까요? src/async.ts
파일에 다음 함수를 추가해볼게요:
export function fetchDataCallback(callback: (data: string) => void): void {
setTimeout(() => {
callback('콜백으로 데이터 가져오기 성공!');
}, 1000);
}
이 함수를 테스트하려면 Jest의 done
함수를 사용해야 해요. src/__tests__/async.test.ts
파일에 다음 테스트를 추가해주세요:
import { fetchData, fetchDataCallback } from '../async';
describe('async functions', () => {
it('should return data after 1 second', async () => {
const data = await fetchData();
expect(data).toBe('데이터 가져오기 성공!');
});
it('should return data via callback after 1 second', (done) => {
fetchDataCallback((data) => {
expect(data).toBe('콜백으로 데이터 가져오기 성공!');
done();
});
});
});
done
함수는 Jest에게 "이 테스트는 이 함수가 호출될 때까지 끝나지 않았어!"라고 알려주는 역할을 해요. 콜백이 실행되면 done()
을 호출해서 테스트를 종료하는 거죠.
4.3 에러 처리 테스트하기
비동기 함수에서 발생하는 에러도 테스트해야 해요. 에러 처리는 정말 중요하거든요! src/async.ts
파일에 다음 함수를 추가해볼게요:
export function fetchDataWithError(): Promise<string> {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('데이터 가져오기 실패!'));
}, 1000);
});
}
</string>
이 함수는 항상 에러를 발생시켜요. 이런 경우를 어떻게 테스트할 수 있을까요? src/__tests__/async.test.ts
파일에 다음 테스트를 추가해주세요:
import { fetchData, fetchDataCallback, fetchDataWithError } from '../async';
describe('async functions', () => {
// ... 이전 테스트들 ...
it('should throw an error', async () => {
await expect(fetchDataWithError()).rejects.toThrow('데이터 가져오기 실패!');
});
});
여기서는 rejects
와 toThrow
매처를 사용해서 Promise가 거부되고, 특정 에러 메시지를 던지는지 확인하고 있어요.
4.4 타임아웃 설정하기
때로는 비동기 테스트가 너무 오래 걸릴 수 있어요. 이런 경우를 대비해 Jest는 타임아웃 설정을 제공해요. 기본값은 5초인데, 이를 변경할 수 있어요. src/__tests__/async.test.ts
파일에 다음 테스트를 추가해볼까요?
describe('async functions', () => {
// ... 이전 테스트들 ...
it('should timeout after 3 seconds', async () => {
jest.setTimeout(3000); // 3초로 타임아웃 설정
await new Promise(resolve => setTimeout(resolve, 4000)); // 4초 대기
}, 3000); // 이 테스트의 타임아웃을 3초로 설정
});
이 테스트는 3초 후에 타임아웃 에러를 발생시킬 거예요. 왜냐하면 테스트는 4초를 기다리지만, 우리가 타임아웃을 3초로 설정했기 때문이에요.
비동기 코드 테스트, 생각보다 어렵지 않죠? 😄 이렇게 비동기 코드도 꼼꼼하게 테스트할 수 있어요. 마치 재능넷에서 복잡한 기술을 배우는 것처럼, 처음엔 어려워 보여도 차근차근 해나가면 충분히 할 수 있답니다!
다음 섹션에서는 모의 객체(Mocks)를 사용한 테스트에 대해 알아볼 거예요. 기대되지 않나요? 🚀
5. 모의 객체(Mocks)를 사용한 테스트 🎭
자, 이제 정말 흥미진진한 부분이에요! 모의 객체(Mocks)를 사용한 테스트에 대해 알아볼 거예요. 모의 객체란 뭘까요? 쉽게 말해, 진짜 객체를 흉내 내는 가짜 객체예요. 마치 연극에서 배우가 다른 사람인 척 하는 것처럼요! ㅋㅋㅋ
5.1 왜 모의 객체를 사용할까요?
모의 객체를 사용하는 이유는 여러 가지가 있어요:
- 외부 의존성을 제거할 수 있어요 (예: 데이터베이스, API 등)
- 테스트 실행 속도를 높일 수 있어요
- 특정 상황을 쉽게 시뮬레이션할 수 있어요
- 함수가 어떻게 호출되었는지 추적할 수 있어요
이제 실제로 모의 객체를 만들고 사용해볼까요?
5.2 Jest로 모의 함수 만들기
Jest에서는 jest.fn()
을 사용해 모의 함수를 만들 수 있어요. 간단한 예제를 볼까요? src/__tests__/mock.test.ts
파일을 만들고 다음 코드를 입력해주세요:
describe('Mock function', () => {
it('should create a mock function', () => {
const mockFn = jest.fn();
mockFn();
mockFn(42);
mockFn('a', 'b', 'c');
expect(mockFn).toHaveBeenCalledTimes(3);
expect(mockFn).toHaveBeenCalledWith(42);
expect(mockFn).toHaveBeenLastCalledWith('a', 'b', 'c');
});
});
이 테스트에서는 모의 함수를 만들고, 여러 번 호출한 다음, 그 호출 정보를 확인하고 있어요. 재능넷에서 새로운 기술을 배우고 연습하는 것처럼, 모의 함수도 이렇게 연습할 수 있어요! 😊
5.3 모의 구현 제공하기
때로는 모의 함수가 특정 값을 반환하게 하고 싶을 수 있어요. 이럴 때는 mockReturnValue
나 mockImplementation
을 사용할 수 있어요. 다음 테스트를 추가해볼까요?
describe('Mock implementation', () => {
it('should return a specified value', () => {
const mockFn = jest.fn().mockReturnValue('재능넷');
expect(mockFn()).toBe('재능넷');
});
it('should have a custom implementation', () => {
const mockFn = jest.fn().mockImplementation((a, b) => a + b);
expect(mockFn(2, 3)).toBe(5);
});
});
이렇게 하면 모의 함 수가 우리가 원하는 대로 동작하게 만들 수 있어요. 마치 재능넷에서 원하는 재능을 선택해서 배우는 것처럼 말이죠! 😄
5.4 모듈 모의하기
때로는 전체 모듈을 모의해야 할 때가 있어요. 예를 들어, 외부 API를 호출하는 모듈이 있다고 가정해볼게요. src/api.ts
파일을 만들고 다음 코드를 입력해주세요:
export async function fetchUser(id: number): Promise<string> {
// 실제로는 여기서 API 호출을 하겠지만, 예시를 위해 간단히 구현했어요.
return `User ${id}`;
}
</string>
이제 이 모듈을 사용하는 함수를 만들어볼게요. src/user.ts
파일을 만들고 다음 코드를 입력해주세요:
import { fetchUser } from './api';
export async function getUppercaseUsername(id: number): Promise<string> {
const name = await fetchUser(id);
return name.toUpperCase();
}
</string>
이제 getUppercaseUsername
함수를 테스트하고 싶은데, 실제 API를 호출하고 싶지 않아요. 이럴 때 모듈 모의를 사용할 수 있어요. src/__tests__/user.test.ts
파일을 만들고 다음 코드를 입력해주세요:
import { getUppercaseUsername } from '../user';
import { fetchUser } from '../api';
jest.mock('../api');
describe('getUppercaseUsername', () => {
it('should return uppercase username', async () => {
// fetchUser 함수를 모의 구현으로 대체해요
(fetchUser as jest.MockedFunction<typeof fetchuser>).mockResolvedValue('john');
const result = await getUppercaseUsername(1);
expect(fetchUser).toHaveBeenCalledWith(1);
expect(result).toBe('JOHN');
});
});
</typeof>
여기서 jest.mock('../api')
는 api.ts
모듈 전체를 모의 모듈로 대체해요. 그리고 mockResolvedValue
를 사용해 fetchUser
함수가 'john'을 반환하도록 설정했어요.
5.5 부분적 모의
때로는 모듈의 일부만 모의하고 싶을 때가 있어요. 이럴 때는 jest.spyOn
을 사용할 수 있어요. 다음 테스트를 추가해볼까요?
import * as api from '../api';
describe('Partial mock', () => {
it('should mock only one function', async () => {
const spy = jest.spyOn(api, 'fetchUser').mockResolvedValue('jane');
const result = await api.fetchUser(2);
expect(spy).toHaveBeenCalledWith(2);
expect(result).toBe('jane');
spy.mockRestore(); // 원래 구현으로 되돌려요
});
});
이 방법을 사용하면 모듈의 다른 부분은 그대로 두고 특정 함수만 모의할 수 있어요. 마치 재능넷에서 특정 강의만 선택해서 들을 수 있는 것처럼요! 👍
5.6 타이머 모의하기
마지막으로, 시간과 관련된 함수들(setTimeout, setInterval 등)을 모의하는 방법을 알아볼게요. Jest는 이를 위해 가짜 타이머를 제공해요. src/timer.ts
파일을 만들고 다음 코드를 입력해주세요:
export function delayedHello(callback: (message: string) => void): void {
setTimeout(() => {
callback('Hello after 1 second');
}, 1000);
}
이제 이 함수를 테스트해볼까요? src/__tests__/timer.test.ts
파일을 만들고 다음 코드를 입력해주세요:
import { delayedHello } from '../timer';
describe('Timer mocks', () => {
beforeEach(() => {
jest.useFakeTimers();
});
afterEach(() => {
jest.useRealTimers();
});
it('should call the callback after 1 second', () => {
const callback = jest.fn();
delayedHello(callback);
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(1000);
expect(callback).toHaveBeenCalledWith('Hello after 1 second');
});
});
여기서 jest.useFakeTimers()
는 Jest에게 가짜 타이머를 사용하라고 지시해요. 그리고 jest.advanceTimersByTime(1000)
으로 시간을 1초 앞당겼어요. 이렇게 하면 실제로 1초를 기다리지 않고도 타이머 동작을 테스트할 수 있어요!
와우! 모의 객체에 대해 정말 많이 배웠네요. 😃 이제 여러분은 다양한 상황에서 모의 객체를 사용할 수 있을 거예요. 마치 재능넷에서 다양한 재능을 익히는 것처럼, 테스트에서도 다양한 기술을 사용할 수 있게 됐어요!
다음 섹션에서는 테스트 커버리지에 대해 알아볼 거예요. 우리가 작성한 테스트가 코드의 얼마나 많은 부분을 커버하고 있는지 확인하는 방법을 배워볼 거예요. 기대되지 않나요? 🚀
6. 테스트 커버리지 확인하기 📊
자, 이제 우리가 작성한 테스트가 얼마나 효과적인지 알아볼 시간이에요! 테스트 커버리지란, 우리의 테스트 코드가 실제 코드의 얼마나 많은 부분을 테스트하고 있는지를 나타내는 지표예요. 마치 재능넷에서 학습 진도율을 확인하는 것과 비슷하죠! 😄
6.1 Jest에서 커버리지 리포트 생성하기
Jest는 기본적으로 커버리지 리포트 생성 기능을 제공해요. package.json
파일에 다음 스크립트를 추가해볼까요?
"scripts": {
...
"test:coverage": "jest --coverage"
}
이제 터미널에서 다음 명령어를 실행해보세요:
npm run test:coverage
그러면 다음과 같은 커버리지 리포트를 볼 수 있을 거예요:
--------------------|---------|----------|---------|---------|------------------- File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s --------------------|---------|----------|---------|---------|------------------- All files | 85.71 | 100 | 100 | 85.71 | async.ts | 100 | 100 | 100 | 100 | math.ts | 100 | 100 | 100 | 100 | timer.ts | 50 | 100 | 100 | 50 | 3 --------------------|---------|----------|---------|---------|-------------------
이 리포트는 각 파일별로 구문(Statements), 분기(Branches), 함수(Functions), 라인(Lines)의 커버리지를 보여줘요. 100%에 가까울수록 좋지만, 항상 100%를 목표로 할 필요는 없어요. 중요한 부분을 잘 커버하고 있는지가 더 중요해요!
6.2 커버리지 리포트 해석하기
커버리지 리포트를 자세히 살펴볼까요?
- % Stmts (구문): 코드의 각 구문이 실행되었는지를 나타내요.
- % Branch (분기): if문 같은 조건문의 각 분기가 테스트되었는지를 나타내요.
- % Funcs (함수): 각 함수가 호출되었는지를 나타내요.
- % Lines (라인): 각 라인이 실행되었는지를 나타내요.
- Uncovered Line #s: 테스트되지 않은 라인 번호를 보여줘요.
우리의 리포트를 보면, timer.ts
파일의 커버리지가 50%네요. 이 파일의 테스트를 좀 더 보완할 필요가 있어 보여요!
6.3 커버리지 개선하기
timer.ts
파일의 커버리지를 개선해볼까요? 현재 우리는 콜백이 호출되는지만 테스트했어요. 콜백이 호출되지 않는 경우도 테스트해볼 수 있겠죠. src/__tests__/timer.test.ts
파일에 다음 테스트를 추가해볼게요:
it('should not call the callback before 1 second', () => {
const callback = jest.fn();
delayedHello(callback);
jest.advanceTimersByTime(999); // 1초 미만으로 시간을 앞당깁니다.
expect(callback).not.toBeCalled();
});
이제 다시 커버리지 리포트를 실행해보면, timer.ts
파일의 커버리지가 100%가 됐을 거예요!
6.4 커버리지 목표 설정하기
프로젝트에 따라 최소 커버리지 목표를 설정할 수 있어요. jest.config.js
파일에 다음 설정을 추가해볼까요?
module.exports = {
// ... 기존 설정 ...
coverageThreshold: {
global: {
branches: 80,
functions: 80,
lines: 80,
statements: 80
}
}
};
이렇게 하면 커버리지가 80% 미만일 경우 테스트가 실패하게 돼요. 이는 팀 전체가 테스트의 중요성을 인식하고 품질 높은 테스트를 작성하도록 동기부여가 될 수 있어요.
6.5 커버리지 리포트 시각화하기
Jest는 HTML 형식의 상세한 커버리지 리포트도 생성할 수 있어요. package.json
의 스크립트를 다음과 같이 수정해볼까요?
"scripts": {
...
"test:coverage": "jest --coverage --coverageReporters='text-summary' --coverageReporters='html'"
}
이제 npm run test:coverage
를 실행하면, 프로젝트 루트의 coverage
폴더에 HTML 리포트가 생성돼요. 이 리포트를 브라우저에서 열어보면 각 파일의 커버리지를 시각적으로 확인할 수 있어요. 테스트된 라인은 초록색으로, 테스트되지 않은 라인은 빨간색으로 표시되죠.
와우! 이제 우리는 테스트 커버리지에 대해 정말 많이 알게 됐어요. 🎉 커버리지 리포트를 통해 우리의 테스트가 얼마나 효과적인지 확인하고, 부족한 부분을 보완할 수 있게 됐죠. 마치 재능넷에서 학습 현황을 체크하고 부족한 부분을 보충하는 것처럼요!
다음 섹션에서는 지금까지 배운 내용을 종합해서 실제 프로젝트에 적용하는 방법에 대해 알아볼 거예요. 정말 기대되지 않나요? 🚀
7. 실제 프로젝트에 적용하기 🏗️
자, 이제 우리가 배운 모든 것을 종합해서 실제 프로젝트에 적용해볼 시간이에요! 마치 재능넷에서 배운 재능을 실제 생활에서 활용하는 것처럼요. 😊 간단한 투두 리스트 애플리케이션을 만들고 테스트해볼게요.
7.1 프로젝트 구조 설정
먼저 프로젝트 구조를 다음과 같이 설정해볼게요:
src/
models/
Todo.ts
services/
TodoService.ts
__tests__/
TodoService.test.ts
7.2 Todo 모델 만들기
src/models/Todo.ts
파일을 만들고 다음 코드를 입력해주세요:
export interface Todo {
id: number;
text: string;
completed: boolean;
}
7.3 TodoService 구현하기
src/services/TodoService.ts
파일을 만들고 다음 코드를 입력해주세요:
import { Todo } from '../models/Todo';
export class TodoService {
private todos: Todo[] = [];
addTodo(text: string): Todo {
const newTodo: Todo = {
id: this.todos.length + 1,
text,
completed: false
};
this.todos.push(newTodo);
return newTodo;
}
getTodos(): Todo[] {
return this.todos;
}
toggleTodo(id: number): Todo | undefined {
const todo = this.todos.find(t => t.id === id);
if (todo) {
todo.completed = !todo.completed;
}
return todo;
}
deleteTodo(id: number): boolean {
const initialLength = this.todos.length;
this.todos = this.todos.filter(t => t.id !== id);
return this.todos.length !== initialLength;
}
}
7.4 TodoService 테스트 작성하기
이제 src/__tests__/TodoService.test.ts
파일을 만들고 다음과 같이 테스트를 작성해볼게요:
import { TodoService } from '../services/TodoService';
describe('TodoService', () => {
let todoService: TodoService;
beforeEach(() => {
todoService = new TodoService();
});
it('should add a new todo', () => {
const todo = todoService.addTodo('Test todo');
expect(todo.text).toBe('Test todo');
expect(todo.completed).toBe(false);
expect(todoService.getTodos()).toHaveLength(1);
});
it('should toggle a todo', () => {
const todo = todoService.addTodo('Test todo');
const toggled = todoService.toggleTodo(todo.id);
expect(toggled?.completed).toBe(true);
});
it('should delete a todo', () => {
const todo = todoService.addTodo('Test todo');
const deleted = todoService.deleteTodo(todo.id);
expect(deleted).toBe(true);
expect(todoService.getTodos()).toHaveLength(0);
});
it('should return undefined when toggling non-existent todo', () => {
const toggled = todoService.toggleTodo(999);
expect(toggled).toBeUndefined();
});
it('should return false when deleting non-existent todo', () => {
const deleted = todoService.deleteTodo(999);
expect(deleted).toBe(false);
});
});
7.5 테스트 실행 및 커버리지 확인
이제 다음 명령어로 테스트를 실행하고 커버리지를 확인해볼까요?
npm run test:coverage
모든 테스트가 통과하고 높은 커버리지를 달성했다면 성공이에요! 🎉
7.6 지속적 통합 (CI) 설정
마지막으로, GitHub Actions를 사용해 지속적 통합을 설정해볼게요. 프로젝트 루트에 .github/workflows/test.yml
파일을 만들고 다음 내용을 입력해주세요:
name: Run Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: '14'
- run: npm ci
- run: npm run test:coverage
이제 GitHub에 코드를 푸시하거나 풀 리퀘스트를 생성할 때마다 자동으로 테스트가 실행될 거예요!
와우! 🎉 우리는 방금 실제 프로젝트에 Jest와 타입스크립트를 사용한 단위 테스트를 성공적으로 적용했어요. 투두 리스트 서비스의 모든 주요 기능을 테스트하고, 높은 커버리지를 달성했으며, 지속적 통합까지 설정했죠. 이제 여러분은 재능넷에서 배운 재능을 실제 프로젝트에 적용한 것과 같은 경험을 했어요!
이러한 방식으로 테스트를 작성하면, 코드의 품질을 높이고 버그를 줄일 수 있어요. 또한 새로운 기능을 추가하거나 기존 코드를 수정할 때도 자신감을 가질 수 있죠. 마치 재능넷에서 새로운 재능을 익힌 후 자신감 있게 그 재능을 뽐내는 것처럼요! 😄
여러분의 다음 프로젝트에서도 이런 방식으로 테스트를 적용해보는 건 어떨까요? 분명 더 나은 개발자로 성장하는 데 큰 도움이 될 거예요! 화이팅! 💪
결론: 테스트의 힘 💪
여러분, 정말 대단해요! 👏 우리는 Jest와 타입스크립트를 사용한 단위 테스트의 긴 여정을 함께 했어요. 마치 재능넷에서 새로운 재능을 배우는 것처럼, 우리도 새로운 기술을 익혔죠.
이 글을 통해 우리가 배운 내용을 정리해볼까요?
- Jest와 타입스크립트의 기본 설정 방법
- 간단한 함수부터 복잡한 비동기 코드까지 다양한 테스트 작성법
- 모의 객체(Mocks)를 활용한 의존성 관리
- 테스트 커버리지 확인 및 개선 방법
- 실제 프로젝트에 테스트 적용하기
테스트를 작성하는 것은 처음에는 시간이 좀 걸리고 어렵게 느껴질 수 있어요. 하지만 장기적으로 봤을 때, 테스트는 여러분의 코드를 더 안정적이고 유지보수하기 쉽게 만들어줘요. 마치 재능넷에서 꾸준히 연습하면 실력이 늘어나는 것처럼 말이에요! 😊
테스트를 작성하면 다음과 같은 이점이 있어요:
- 버그를 미리 발견하고 수정할 수 있어요.
- 코드의 품질과 신뢰성이 높아져요.
- 리팩토링을 자신 있게 할 수 있어요.
- 코드의 동작을 명확하게 문서화할 수 있어요.
- 새로운 팀원이 프로젝트를 이해하는 데 도움이 돼요.
앞으로 여러분이 프로젝트를 진행할 때, 이 글에서 배운 내용을 적용해보세요. 처음에는 어색할 수 있지만, 점점 테스트 작성이 자연스러워질 거예요. 그리고 어느새 여러분은 테스트의 달인이 되어 있을 거예요! 🏆
기억하세요, 좋은 개발자는 기능을 구현하는 것뿐만 아니라 그 기능이 제대로 동작한다는 것을 증명할 수 있어야 해요. 테스트는 그 증명의 핵심이에요. 여러분의 코드에 자신감을 가지세요. 그리고 그 자신감은 잘 작성된 테스트에서 나온다는 것을 잊지 마세요!
자, 이제 여러분은 Jest와 타입스크립트를 이용한 단위 테스트의 전문가가 됐어요. 이 새로운 재능을 여러분의 프로젝트에 마음껏 뽐내보세요. 재능넷에서 새로운 재능을 익히고 그것을 실생활에서 활용하는 것처럼, 여러분도 이 테스트 기술을 실제 프로젝트에 적용해보세요. 분명 더 나은 개발자로 성장하는 데 큰 도움이 될 거예요!
테스트와 함께하는 즐거운 코딩 되세요! 화이팅! 💻🚀