
import { useEffect, useState } from 'react';
interface User {
firstName: string;
lastName: string;
email: string;
}
export default function BadUserProfile() {
const [user, setUser] = useState<User | null>(null);
// [문제 1] 컴포넌트가 '데이터 가져오기'를 직접 수행함 (의존성 높음)
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users/1')
.then((res) => res.json())
.then((data) => {
// [문제 2] 데이터를 변환하는 '비즈니스 로직'이 섞여 있음
const formattedData = {
...data,
// 이름은 대문자로 변환해야 한다는 규칙
firstName: data.name.split(' ')[0].toUpperCase(),
lastName: data.name.split(' ')[1].toUpperCase(),
};
setUser(formattedData);
});
}, []);
if (!user) return <div>로딩 중...</div>;
return (
<div>
{/* [문제 3] 화면 그리기 로직 (UI) */}
<h1>{user.firstName} {user.lastName}</h1>
<p>{user.email}</p>
</div>
);
}fetch를 모킹해야 하고, 비동기 처리를 기다려야(findBy) 합니다.
단순히 "이름이 굵게(h1) 나오는지"만 확인하고 싶은데도 말이죠.props로 받아서 그리기만 합니다.
// 데이터 타입 정의
export interface UserProfileProps {
firstName: string;
lastName: string;
email: string;
}
export default function UserProfileView({ firstName, lastName, email }: UserProfileProps) {
return (
<div>
<h1>{firstName} {lastName}</h1>
<p>{email}</p>
</div>
);
}import { useEffect, useState } from 'react';
import UserProfileView from './UserProfileView';
export default function UserProfileContainer() {
const [user, setUser] = useState(null);
useEffect(() => {
// ... fetch 및 데이터 가공 로직 (혹은 Custom Hook 사용)
// 여기서는 생략합니다.
}, []);
if (!user) return <div>로딩 중...</div>;
// 가공된 데이터를 UI 컴포넌트에 주입
return <UserProfileView {...user} />;
}UserProfileView)를 테스트해 보겠습니다.
fetch나 MSW 같은 복잡한 설정이 전혀 필요 없습니다.import { render, screen } from '@testing-library/react';
import { describe, test, expect } from 'vitest';
import UserProfileView from './UserProfileView';
describe('UserProfileView UI 테스트', () => {
test('props로 받은 이름과 이메일을 정확히 표시한다', () => {
// 1. 가짜 데이터(Mock Data) 준비
// API 호출 없이 우리가 원하는 데이터를 직접 주입합니다.
const mockUser = {
firstName: 'KIM',
lastName: 'CHULSU',
email: 'test@example.com'
};
render(<UserProfileView {...mockUser} />);
// 2. 로그 확인
console.log('[UI Test] 순수 컴포넌트 렌더링 확인');
screen.debug();
// 3. 검증
// h1 태그 안에 이름이 들어있는지 확인
expect(screen.getByRole('heading')).toHaveTextContent('KIM CHULSU');
// 이메일이 문서에 존재하는지 확인
expect(screen.getByText('test@example.com')).toBeInTheDocument();
});
});npm test -- src/components/UserProfileView.test.tsx
[UI Test] 순수 컴포넌트 렌더링 확인
<body>
<div>
<div>
<h1>
KIM CHULSU
</h1>
<p>
test@example.com
</p>
</div>
</div>
</body>
PASS src/components/UserProfileView.test.tsxfetch 로딩 상태나 에러 메시지 없이, 우리가 주입한 KIM CHULSU 데이터가 깔끔하게 렌더링 된 것을 볼 수 있습니다.await findBy...)가 없으므로 테스트가 순식간에 끝납니다.git clone https://github.com/PROMLEE/my-tdd-app.git
cd my-tdd-app
git checkout part11다음 시간에는12편. 리팩토링을 지원하는 테스트 구성을 통해, 디자인이나 내부 구현이 바뀌어도 깨지지 않는 테스트를 만드는 비결을 알아보겠습니다.