Jest는 테스트 실행기 + 검증 도구
Jest
By ensuring your tests have unique global state, Jest can reliably run tests in parallel. To make things quick, Jest runs previously failed tests first and re-organizes runs based on how long test files take.
jestjs.io
React Testing Library (RTL)는 React 컴포넌트 렌더링 & DOM 쿼리 도구
Testing Library | Testing Library
Simple and complete testing utilities that encourage good testing practices
testing-library.com
Jest, React Testing Library (RTL)를 어디에 어떻게 사용하는지, 각 상황에 맞는 예제 코드
유틸 함수
✔ 대상: Jest
❌ 대상 아님: RTL
예제 대상: utils/price.ts
// price.ts
export function getDiscountPrice(price: number, discountRate: number): number {
return price - price * discountRate;
}
Jest 테스트
// price.test.ts
import { getDiscountPrice } from './price';
test('할인 가격 계산', () => {
expect(getDiscountPrice(10000, 0.1)).toBe(9000);
});
API 호출 함수 (service)
✔ 대상: Jest
❌ 대상 아님: RTL
예제 대상: auth.service.ts
export const loginApi = async ({ email, password }: { email: string; password: string }) => {
const res = await fetch('/api/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
return res.json();
};
Jest 테스트 (Mock 사용)
// auth.service.test.ts
import { loginApi } from './auth.service';
global.fetch = vi.fn(() =>
Promise.resolve({ json: () => Promise.resolve({ accessToken: 'abc' }) })
) as jest.Mock;
test('로그인 API 호출', async () => {
const res = await loginApi({ email: 'a@b.com', password: '1234' });
expect(res.accessToken).toBe('abc');
});
Custom Hook
✔ 대상: Jest (단순 로직)
🔄 대상: RTL (컴포넌트 안에서 동작 확인)
예제 대상: useCounter.ts
import { useState } from 'react';
export const useCounter = () => {
const [count, setCount] = useState(0);
const increment = () => setCount((c) => c + 1);
return { count, increment };
};
Jest + RTL 테스트 (hook 전용)
// useCounter.test.tsx
import { renderHook, act } from '@testing-library/react';
import { useCounter } from './useCounter';
test('카운터 증가', () => {
const { result } = renderHook(() => useCounter());
act(() => {
result.current.increment();
});
expect(result.current.count).toBe(1);
});
컴포넌트 렌더링
❌ 대상 아님: Jest
✔ 대상: RTL
예제 대상: Greeting.tsx
const Greeting = () => <h1>Hello, user!</h1>;
export default Greeting;
RTL 테스트
// Greeting.test.tsx
import { render, screen } from '@testing-library/react';
import Greeting from './Greeting';
test('인사 메시지가 렌더링됨', () => {
render(<Greeting />);
expect(screen.getByText(/hello, user/i)).toBeInTheDocument();
});
사용자 입력/클릭
❌ 대상 아님: Jest
✔ 대상: RTL
예제 대상: Counter.tsx
export const Counter = () => {
const [count, setCount] = useState(0);
return (
<>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>증가</button>
</>
);
};
RTL 테스트
import { render, screen, fireEvent } from '@testing-library/react';
import { Counter } from './Counter';
test('버튼 클릭 시 카운트 증가', () => {
render(<Counter />);
fireEvent.click(screen.getByText(/증가/));
expect(screen.getByText(/count: 1/i)).toBeInTheDocument();
});
전체 로그인/회원가입 흐름
🔄 Jest: 토큰 저장 등 단위 단위 처리
✔ RTL: 사용자가 실제로 로그인하는 흐름
예제 대상: Login.tsx
export const Login = ({ onLogin }: { onLogin: (token: string) => void }) => {
const [email, setEmail] = useState('');
return (
<form
onSubmit={(e) => {
e.preventDefault();
onLogin('mockToken');
}}>
<input
type="email"
placeholder="email"
onChange={(e) => setEmail(e.target.value)}
value={email}
/>
<button type="submit">로그인</button>
</form>
);
};
RTL 테스트 (로그인 시 버튼 클릭 → 콜백 호출 확인)
import { render, screen, fireEvent } from '@testing-library/react';
import { Login } from './Login';
test('로그인 흐름', () => {
const onLogin = vi.fn();
render(<Login onLogin={onLogin} />);
fireEvent.change(screen.getByPlaceholderText(/email/i), {
target: { value: 'test@a.com' },
});
fireEvent.click(screen.getByText(/로그인/));
expect(onLogin).toHaveBeenCalledWith('mockToken');
});
- 중간 난이도 예제: 로그인 컴포넌트에서 버튼 클릭 → API 요청 → 상태 변경 확인
- 고난이도 예제: 비동기 React Query 기반 커스텀 훅 + UI 렌더링 + 상태 갱신까지 포함
예제 1: 로그인 컴포넌트 (Jest + RTL 조합) — 중간 난이도
구조
components/
└─ Login.tsx
services/
└─ auth.service.ts
__tests__/
└─ Login.test.tsx
auth.service.ts
export const loginApi = async (email: string, password: string) => {
return Promise.resolve({ accessToken: 'abc-token' });
};
Login.tsx
import { useState } from 'react';
import { loginApi } from '../services/auth.service';
export const Login = ({ onLogin }: { onLogin: (token: string) => void }) => {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const handleLogin = async () => {
const res = await loginApi(email, password);
onLogin(res.accessToken);
};
return (
<div>
<input
placeholder="Email"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
placeholder="Password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button onClick={handleLogin}>Login</button>
</div>
);
};
Login.test.tsx (Jest + RTL)
import { render, screen, fireEvent } from '@testing-library/react';
import { Login } from '../components/Login';
import * as auth from '../services/auth.service';
test('로그인 API 호출 후 토큰 수신', async () => {
const mockLogin = vi.spyOn(auth, 'loginApi').mockResolvedValue({
accessToken: 'test-token',
});
const onLogin = vi.fn();
render(<Login onLogin={onLogin} />);
fireEvent.change(screen.getByPlaceholderText(/email/i), {
target: { value: 'test@email.com' },
});
fireEvent.change(screen.getByPlaceholderText(/password/i), {
target: { value: '1234' },
});
fireEvent.click(screen.getByText(/login/i));
// wait for async behavior
await screen.findByText(/login/i);
expect(mockLogin).toHaveBeenCalledWith('test@email.com', '1234');
expect(onLogin).toHaveBeenCalledWith('test-token');
});
예제 2: React Query + Zustand + UI 렌더링 — 고난이도
시나리오
- 서버에서 유저 정보 가져오기
- Zustand에 저장
- 유저 이름 렌더링
- 버튼 클릭으로 상태 초기화
구조
features/
└─ user/
├─ user.store.ts
├─ useUserQuery.ts
└─ UserProfile.tsx
__tests__/
└─ UserProfile.test.tsx
user.store.ts
import { create } from 'zustand';
type User = { name: string };
type State = {
user: User | null;
setUser: (u: User) => void;
clear: () => void;
};
export const useUserStore = create<State>((set) => ({
user: null,
setUser: (u) => set({ user: u }),
clear: () => set({ user: null }),
}));
useUserQuery.ts
import { useQuery } from '@tanstack/react-query';
export const useUserQuery = () =>
useQuery({
queryKey: ['user'],
queryFn: async () => {
const res = await fetch('/api/user');
return res.json(); // { name: "Heeseong" }
},
});
UserProfile.tsx
import { useUserQuery } from './useUserQuery';
import { useUserStore } from './user.store';
export const UserProfile = () => {
const { data } = useUserQuery();
const user = useUserStore((s) => s.user);
const setUser = useUserStore((s) => s.setUser);
const clear = useUserStore((s) => s.clear);
if (!user && data) setUser(data);
return (
<div>
<h1>{user?.name || 'Unknown'}</h1>
<button onClick={clear}>Logout</button>
</div>
);
};
UserProfile.test.tsx
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { useUserStore } from '@/features/user/user.store';
import { UserProfile } from '@/features/user/UserProfile';
const queryClient = new QueryClient();
global.fetch = vi.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ name: 'Heeseong' }),
})
) as any;
beforeEach(() => {
useUserStore.getState().clear();
});
test('유저 이름을 불러오고 로그아웃 시 상태 초기화', async () => {
render(
<QueryClientProvider client={queryClient}>
<UserProfile />
</QueryClientProvider>
);
await waitFor(() => {
expect(screen.getByText('Heeseong')).toBeInTheDocument();
});
fireEvent.click(screen.getByText(/logout/i));
expect(screen.getByText('Unknown')).toBeInTheDocument();
});
Jest는 언제 사용하는가?
비즈니스 로직 / 유틸 함수 / API / 상태 변경을 테스트할 때
// utils/calc.ts
export const sum = (a: number, b: number) => a + b;
test('sum 함수 테스트', () => {
expect(sum(2, 3)).toBe(5); // ✅ Jest만으로 충분
});
사용하는 기능
- test(), describe(), expect()
- jest.fn(), mockReturnValue()
- beforeEach(), afterAll()
- resolves, rejects (비동기 처리)
컴포넌트가 아닌 일반 함수/비동기 로직은 Jest만으로 테스트 가능합니다.
React Testing Library는 언제 사용하는가?
React 컴포넌트의 렌더링, 사용자 상호작용, UI 반응을 테스트할 때
render(<Button label="클릭" onClick={fn} />);
const button = screen.getByText("클릭");
fireEvent.click(button); // ✅ 사용자가 클릭하는 행위를 시뮬레이션
사용하는 기능
- render() : 컴포넌트를 가상 DOM에 렌더링
- screen.getByText(), getByRole() 등 쿼리 함수
- fireEvent, userEvent로 이벤트 발생
- waitFor, findByText()로 비동기 테스트
컴포넌트를 테스트할 땐 반드시 React Testing Library가 필요
대신 검증(expect)은 Jest를 사용 → 그래서 항상 두 도구를 같이 쓰는 것처럼 보이는 것
| 함수, 유틸 로직 | Jest 단독 | sum(), formatDate() 등 |
| API 호출 테스트 (with MSW) | Jest + mock 도구 | fetchUser() 등 |
| 컴포넌트 렌더링 | Jest + RTL | render(<MyComponent />) |
| UI에 특정 텍스트가 있는지 | Jest + RTL | getByText("로그인") |
| 버튼 클릭 이벤트 처리 | Jest + RTL | fireEvent.click(button) |
| 비동기 렌더링 확인 | Jest + RTL + findByText | 로딩 후 "완료" 메시지 확인 등 |
// Jest + RTL 조합
test('버튼 클릭 시 텍스트 변경', () => {
render(<MyButton />);
fireEvent.click(screen.getByText('클릭')); // RTL
expect(screen.getByText('완료')).toBeInTheDocument(); // Jest + RTL
});
1. Jest의 역할
| Test Runner | .test.js 또는 .spec.js 파일을 찾아 실행 |
| Assertion Library | expect() 구문으로 결과를 검증 |
| Mocking 기능 | 의존성을 가짜로 만들어 테스트 가능하게 함 |
2. 기본 사용 구조
test('설명', () => {
// 1. 준비 (Arrange)
// 2. 실행 (Act)
// 3. 검증 (Assert)
});
예시:
test('1 + 2는 3이다', () => {
const result = 1 + 2;
expect(result).toBe(3);
});
Jest 폴더
__폴더명__(예: __tests__, __mocks__, __snapshots__)처럼 언더스코어(__)로 감싼 폴더명은 특별한 의미가 있는 관례적인 구조
| __tests__ | ❌ 선택 | 코드와 테스트를 분리하고 싶을 때 |
| __mocks__ | 🔶 조건부 | Jest의 자동 mock 기능을 쓸 경우 |
| __snapshots__ | ✅ 자동 생성 | 스냅샷 테스트를 사용할 경우에만 |
→ 반드시 써야 하는 건 아니고, 구조적 정리가 필요할 때 선택적으로 사용
3. 자주 쓰는 Jest 메서드 & 기능
test 또는 it
test('설명', () => {...});
it('설명', () => {...}); // 동일함
expect()
expect(실제값).matcher(예상값) 형태로 작성
| .toBe(value) | === 값 비교 |
| .toEqual(obj) | 객체 값 비교 (deep comparison) |
| .toContain(item) | 배열 또는 문자열 포함 여부 |
| .toHaveLength(n) | 길이 검사 |
| .toBeTruthy() / .toBeFalsy() | 참/거짓 여부 |
| .toBeNull() / .toBeUndefined() | null 또는 undefined 여부 |
| .toBeGreaterThan(n) | 숫자 비교 |
| .toMatch(regex) | 문자열 정규표현식 비교 |
| .toHaveBeenCalled() | mock 함수 호출 여부 |
| .toHaveBeenCalledTimes(n) | 몇 번 호출됐는지 |
| .toHaveBeenCalledWith(arg) | 어떤 인자로 호출됐는지 |
4. 구조화 도우미
describe()
- 관련 테스트들을 그룹화
describe('덧셈 함수', () => {
test('1 + 1 = 2', () => { ... });
test('0 + 0 = 0', () => { ... });
});
beforeEach / afterEach
- 매 테스트 전후에 공통 동작 실행
beforeEach(() => {
console.log('매 테스트마다 실행됨');
});
beforeAll / afterAll
- 전체 테스트 한 번만 실행
beforeAll(() => {
console.log('테스트 시작 전 한 번 실행');
});
5. Mock 관련 메서드
jest.fn()
- 가짜 함수(mock 함수)를 생성
const mockFn = jest.fn();
mockFn('hello');
expect(mockFn).toHaveBeenCalledWith('hello');
jest.mock()
- 외부 모듈 전체를 mock 처리
jest.mock('../api/userAPI'); // userAPI 모듈을 자동으로 mock 처리
mockReturnValue()
const mock = jest.fn().mockReturnValue(42);
expect(mock()).toBe(42);
mockResolvedValue() / mockRejectedValue()
- 비동기 함수의 반환값도 설정 가능
const fetchUser = jest.fn().mockResolvedValue({ name: 'mars112' });
const failAPI = jest.fn().mockRejectedValue(new Error('실패!'));
6. 비동기 테스트
1) async/await 사용
test('비동기 함수가 값을 반환해야 한다', async () => {
const fetchData = () => Promise.resolve(42);
const result = await fetchData();
expect(result).toBe(42);
});
2) .resolves / .rejects
test('resolves matcher 사용', () => {
return expect(Promise.resolve('OK')).resolves.toBe('OK');
});
test('rejects matcher 사용', () => {
return expect(Promise.reject('에러')).rejects.toBe('에러');
});
유틸 함수 테스트
// utils/math.ts
export const add = (a: number, b: number) => a + b;
// math.test.ts
import { add } from './math';
test('add는 두 수를 더한다', () => {
expect(add(2, 3)).toBe(5);
});
컴포넌트 테스트 (React Testing Library 사용)
// Button.tsx
export const Button = ({ onClick }: { onClick: () => void }) => (
<button onClick={onClick}>클릭</button>
);
// Button.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { Button } from './Button';
test('버튼 클릭 시 함수가 호출됨', () => {
const onClick = jest.fn();
render(<Button onClick={onClick} />);
fireEvent.click(screen.getByText('클릭'));
expect(onClick).toHaveBeenCalledTimes(1);
});
8. Jest 설정 파일 (jest.config.ts)
export default {
testEnvironment: 'jsdom', // 브라우저 환경
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
moduleNameMapper: {
'^@/(.*)$': '<rootDir>/src/$1', // 절대경로 import 지원
},
};
실무에 바로 쓸 수 있게 정리
1. setupTests.ts 구성
테스트 환경을 초기화하는 전역 설정 파일입니다.
보통 @testing-library/jest-dom 같은 확장 matcher를 불러오거나, 공통 mock을 여기에 정의해요.
파일 위치
src/
├── setupTests.ts ← 여기에 설정
예시
// src/setupTests.ts
import '@testing-library/jest-dom'; // toBeInTheDocument 등 확장 matcher 지원
jest.config.ts에 등록 필요
// jest.config.ts
export default {
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
};
2. Mock Server 연동 테스트 (MSW 사용)
**Mock Service Worker (MSW)**를 사용하면 fetch, axios 요청을 가짜 서버로 처리 가능
실제 API가 없어도 테스트 가능하고, 비동기 테스트에서 매우 유용합니다.
설치
npm install msw --save-dev
구조
src/
├── mocks/
│ ├── handlers.ts
│ └── server.ts
├── setupTests.ts
handlers.ts
import { rest } from 'msw';
export const handlers = [
rest.get('/api/user', (req, res, ctx) => {
return res(ctx.status(200), ctx.json({ name: 'mars112' }));
}),
];
server.ts
import { setupServer } from 'msw/node';
import { handlers } from './handlers';
export const server = setupServer(...handlers);
setupTests.ts
import '@testing-library/jest-dom';
import { server } from './mocks/server';
// 테스트 전체 전/후 훅 등록
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
테스트
import { render, screen } from '@testing-library/react';
import { useEffect, useState } from 'react';
const User = () => {
const [user, setUser] = useState(null);
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => setUser(data));
}, []);
return <div>{user ? user.name : '로딩 중...'}</div>;
};
test('유저 데이터를 렌더링한다', async () => {
render(<User />);
const name = await screen.findByText('mars112');
expect(name).toBeInTheDocument();
});
3. Jest로 TDD
TDD(Test-Driven Development): “테스트 먼저 쓰고 → 코드를 작성해서 통과시킴”
이메일 유효성 검사 함수
Step 1: 테스트부터 작성 (email.test.ts)
import { validateEmail } from './email'; // 아직 없음
test('올바른 이메일은 통과해야 한다', () => {
expect(validateEmail('abc@example.com')).toBe(true);
});
test('잘못된 이메일은 실패해야 한다', () => {
expect(validateEmail('hello@wrong')).toBe(false);
expect(validateEmail('no-at-symbol.com')).toBe(false);
});
테스트 실패 (validateEmail이 아직 없음)
Step 2: 함수 생성 & 테스트 통과시키기 (email.ts)
export const validateEmail = (email: string) => {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
};
npm test → 통과됨!
jest test code 작성 순서
- 실패하는 테스트 작성
- 테스트를 통과할 최소한의 코드 작성
- 통과 후 리팩토링
- 기능 안정성과 의도 확인 가능
1. React Testing Library(RTL)의 역할
| 렌더링 도구 | render()로 컴포넌트를 가상 DOM에 마운트 |
| 쿼리 함수 | 실제 사용자처럼 텍스트/레이블 등을 기준으로 DOM 요소 찾기 |
| 이벤트 시뮬레이션 | fireEvent, userEvent로 클릭/입력/포커스 등 실행 |
| 비동기 처리 지원 | findBy, waitFor로 비동기 렌더링 대응 가능 |
2. 기본 사용 구조
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
test('설명', () => {
render(<컴포넌트 />);
const 요소 = screen.getByText('텍스트');
userEvent.click(요소);
expect(요소).toBeInTheDocument();
});
3. 자주 쓰는 메서드 & 쿼리 함수
render()
- 컴포넌트를 테스트 환경에서 가상 DOM에 마운트
render(<MyComponent />);
screen의 쿼리 함수들
| getByText() | 텍스트로 요소 찾기 (하나만 있을 때) |
| getByRole() | 역할(버튼, heading 등)으로 찾기 |
| getByLabelText() | <label>과 연결된 input 찾기 |
| getByPlaceholderText() | placeholder로 찾기 |
| getByTestId() | data-testid 속성으로 찾기 |
| queryBy... | 요소가 없을 수도 있을 때 |
| findBy... | 비동기적으로 기다렸다가 찾기 (Promise) |
expect(screen.getByText("제목")).toBeInTheDocument();
이벤트 도구
fireEvent
- 기본 DOM 이벤트 (예: click, change)
fireEvent.click(screen.getByText('클릭'));
userEvent
- 실제 사용자 행동에 가까운 고급 시뮬레이션
await userEvent.type(input, 'Hello');
await userEvent.click(button);
userEvent는 실제로 input에 포커스 → 타이핑 → blur 순으로 작동합니다.
4. 비동기 처리
test('로딩 후 데이터 표시', async () => {
render(<UserProfile />);
expect(screen.getByText('로딩 중...')).toBeInTheDocument();
const name = await screen.findByText('mars112');
expect(name).toBeInTheDocument();
});
추가 유틸: waitFor()
await waitFor(() => {
expect(screen.getByText('완료')).toBeInTheDocument();
});
입력폼 테스트
test('입력값이 반영되는가', async () => {
render(<input placeholder="이름 입력" />);
const input = screen.getByPlaceholderText('이름 입력');
await userEvent.type(input, 'mars112');
expect(input).toHaveValue('mars112');
});
버튼 클릭 이벤트
test('버튼 클릭 시 텍스트 변경', async () => {
render(<Button />);
const btn = screen.getByText('시작');
await userEvent.click(btn);
expect(screen.getByText('완료')).toBeInTheDocument();
});
6. 테스트용 속성 - data-testid
CSS class, id 같은 스타일 속성이 아니라, 테스트 전용으로 쓰이는 속성
<div data-testid="user-name">mars112</div>
const name = screen.getByTestId('user-name');
expect(name).toHaveTextContent('mars112');
가능하면 getByText, getByRole 등 사용자 중심 접근 우선 사용 → getByTestId는 최후의 수단
7. 전역 설정 (setupTests.ts)
// setupTests.ts
import '@testing-library/jest-dom'; // 확장 matcher 제공 (toBeInTheDocument 등)
등록 위치 (jest.config.ts)
export default {
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'],
};
8. 확장 matcher (@testing-library/jest-dom)
expect(element).toBeInTheDocument();
expect(button).toBeDisabled();
expect(input).toHaveValue('mars112');
expect(link).toHaveAttribute('href', '/home');
이런 matcher는 jest-dom을 통해 사용!
1. 비동기 컴포넌트 테스트 실전 예제
컴포넌트: User.tsx
import { useEffect, useState } from 'react';
export const User = () => {
const [name, setName] = useState('');
const [loading, setLoading] = useState(true);
useEffect(() => {
fetch('/api/user')
.then(res => res.json())
.then(data => {
setName(data.name);
setLoading(false);
});
}, []);
if (loading) return <p>로딩 중...</p>;
return <h1>안녕하세요, {name}님</h1>;
};
테스트 코드: User.test.tsx (with MSW)
import { render, screen } from '@testing-library/react';
import { User } from './User';
import { rest } from 'msw';
import { setupServer } from 'msw/node';
// Mock server
const server = setupServer(
rest.get('/api/user', (req, res, ctx) =>
res(ctx.status(200), ctx.json({ name: 'mars112' }))
)
);
beforeAll(() => server.listen());
afterEach(() => server.resetHandlers());
afterAll(() => server.close());
test('사용자 데이터를 로딩 후 표시해야 함', async () => {
render(<User />);
expect(screen.getByText('로딩 중...')).toBeInTheDocument();
const name = await screen.findByText('안녕하세요, mars112님');
expect(name).toBeInTheDocument();
});
2. Form Validation 테스트
컴포넌트: LoginForm.tsx
import { useState } from 'react';
export const LoginForm = () => {
const [email, setEmail] = useState('');
const [error, setError] = useState('');
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
if (!email.includes('@')) {
setError('올바른 이메일을 입력하세요');
} else {
setError('');
alert('로그인 성공');
}
};
return (
<form onSubmit={handleSubmit}>
<input
placeholder="이메일"
value={email}
onChange={(e) => setEmail(e.target.value)}
/>
<button type="submit">로그인</button>
{error && <p>{error}</p>}
</form>
);
};
테스트 코드: LoginForm.test.tsx
import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { LoginForm } from './LoginForm';
test('올바르지 않은 이메일 입력 시 오류 메시지 출력', async () => {
render(<LoginForm />);
const input = screen.getByPlaceholderText('이메일');
const button = screen.getByText('로그인');
await userEvent.type(input, 'wrong-email');
await userEvent.click(button);
expect(screen.getByText('올바른 이메일을 입력하세요')).toBeInTheDocument();
});
3. Mocking Axios 요청 테스트
컴포넌트: PostList.tsx
import { useEffect, useState } from 'react';
import axios from 'axios';
export const PostList = () => {
const [posts, setPosts] = useState<string[]>([]);
useEffect(() => {
axios.get('/api/posts').then((res) => setPosts(res.data));
}, []);
return (
<ul>
{posts.map((title, i) => (
<li key={i}>{title}</li>
))}
</ul>
);
};
테스트 코드: PostList.test.tsx
import { render, screen } from '@testing-library/react';
import { PostList } from './PostList';
import axios from 'axios';
jest.mock('axios');
const mockedAxios = axios as jest.Mocked<typeof axios>;
test('axios를 통해 글 목록을 가져와 렌더링', async () => {
mockedAxios.get.mockResolvedValue({ data: ['글1', '글2'] });
render(<PostList />);
const items = await screen.findAllByRole('listitem');
expect(items).toHaveLength(2);
expect(items[0]).toHaveTextContent('글1');
});
'JavaScript > React' 카테고리의 다른 글
| React에서 화면 사이즈 감지하기(반응형 설정하기, window.innerWidth, matchMedia, react-responsive, tailwind) (0) | 2025.03.31 |
|---|---|
| .env 파일과 실행 모드 (1) | 2025.03.28 |
| React 고급 Hook (0) | 2025.03.21 |
| useRef, useImperativeHandle (1) | 2025.03.21 |
| useEffect, useLayoutEffect, useInsertionEffect (0) | 2025.03.21 |