
global.fetch 来进行测试。
但这种方法每次都需要重新定义代码,也难以完整模拟实际的网络行为。npm install -D mswsrc/mocks 文件夹,并在其中创建 handlers.ts 文件。import { http, HttpResponse } from 'msw';
// 가짜 API 주소를 정의합니다.
// 실제 UserList 컴포넌트가 호출하는 URL과 똑같아야 합니다.
export const handlers = [
http.get('https://jsonplaceholder.typicode.com/users', () => {
// 이 URL로 GET 요청이 오면, 아래 JSON 데이터를 돌려줍니다.
return HttpResponse.json([
{ id: 1, name: 'Leanne Graham (MSW)' },
{ id: 2, name: 'Ervin Howell (MSW)' },
]);
}),
];src/mocks/server.ts 文件。import { setupServer } from 'msw/node';
import { handlers } from './handlers';
// 위에서 만든 핸들러들을 묶어서 가짜 서버를 생성합니다.
export const server = setupServer(...handlers);src/setupTests.ts 文件。import '@testing-library/jest-dom';
import { server } from './mocks/server';
import { beforeAll, afterEach, afterAll } from 'vitest';
// [설정] 모든 테스트가 시작되기 전에 가짜 서버를 켭니다.
beforeAll(() => server.listen());
// [정리] 각 테스트가 끝날 때마다 핸들러를 초기화합니다.
// (테스트 중간에 에러 상황으로 바꿨더라도 다시 정상 상태로 되돌립니다)
afterEach(() => server.resetHandlers());
// [종료] 모든 테스트가 끝나면 서버를 끕니다.
afterAll(() => server.close());src/components/UserList.tsx,使其能够处理错误状态。import { useEffect, useState } from 'react';
interface User {
id: number;
name: string;
}
export default function UserList() {
const [users, setUsers] = useState<User[]>([]);
const [loading, setLoading] = useState(true);
// [추가] 에러 메시지를 저장할 상태
const [error, setError] = useState('');
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then((response) => {
if (!response.ok) {
throw new Error('서버 에러가 발생했습니다.');
}
return response.json();
})
.then((data) => {
setUsers(data);
setLoading(false);
})
.catch((err) => {
// [추가] 에러 발생 시 상태 업데이트
setError('에러가 발생했습니다.');
setLoading(false);
});
}, []);
if (loading) return <p>불러오는 중...</p>;
// [추가] 에러 메시지가 있으면 표시
if (error) return <p>{error}</p>;
return (
<ul>
{users.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}UserList.test.tsx。
删除原先所有 global.fetch 代码,使用 server.use 方法,仅在某个特定测试中覆盖处理器import { render, screen } from '@testing-library/react';
import { describe, test, expect } from 'vitest';
import { http, HttpResponse } from 'msw'; // MSW 모듈 import
import { server } from '../mocks/server'; // 가짜 서버 import
import UserList from './UserList';
describe('UserList 컴포넌트 (MSW 적용)', () => {
test('데이터를 불러오는 동안 로딩 문구가 뜨고, 이후 데이터가 표시된다', async () => {
render(<UserList />);
console.log('[Test 1] 로딩 상태 확인');
expect(screen.getByText('불러오는 중...')).toBeInTheDocument();
console.log('[Test 1] MSW 정상 응답 대기');
// handlers.ts에 정의한 "Leanne Graham (MSW)" 데이터가 오길 기다립니다.
const userItem = await screen.findByText('Leanne Graham (MSW)');
console.log('[Test 1] 데이터 수신 완료');
screen.debug();
expect(userItem).toBeInTheDocument();
});
// [추가] 에러 상황 테스트
test('서버 에러 발생 시 에러 문구가 표시된다', async () => {
// 1. 이 테스트 동안만 서버가 500 에러를 뱉도록 설정을 덮어씁니다(Override).
server.use(
http.get('https://jsonplaceholder.typicode.com/users', () => {
return new HttpResponse(null, { status: 500 });
})
);
render(<UserList />);
console.log('[Test 2] 로딩 상태 확인');
expect(screen.getByText('불러오는 중...')).toBeInTheDocument();
console.log('[Test 2] 에러 발생 대기');
// 2. 컴포넌트가 에러를 감지하고 "에러가 발생했습니다."를 띄울 때까지 기다립니다.
const errorMsg = await screen.findByText('에러가 발생했습니다.');
console.log('[Test 2] 에러 UI 렌더링 확인');
screen.debug();
expect(errorMsg).toBeInTheDocument();
});
});npm test -- src/components/UserList.test.tsx
[Test 1] 로딩 상태 확인
[Test 1] MSW 정상 응답 대기
[Test 1] 데이터 수신 완료
<body>
<div>
<ul>
<li>Leanne Graham (MSW)</li>
<li>Ervin Howell (MSW)</li>
</ul>
</div>
</body>
[Test 2] 로딩 상태 확인
[Test 2] 에러 발생 대기
[Test 2] 에러 UI 렌더링 확인
<body>
<div>
<p>에러가 발생했습니다.</p>
</div>
</body>
PASS src/components/UserList.test.tsxserver.use 收到 500 错误并显示错误信息。
无需关闭真实服务器或破坏代码,我们就验证了错误场景。git clone https://github.com/PROMLEE/my-tdd-app.git
cd my-tdd-app
git checkout part9handlers.ts 中统一管理 API 响应,提高了可维护性。setupTests.ts,让模拟服务器自动服务于所有测试文件。server.use,仅在指定测试中强制产生错误,验证组件的应对能力。下一篇将通过第 10 篇:表单输入与校验测试,学习如何测试用户输入文本并点击提交按钮的过程。