
๐ ์ ํ ์คํธ ์ฝ๋๋ฅผ ์์ฑํด์ผ ํ ๊น?
"ํ๋ก ํธ์๋๋ ๋์ผ๋ก ์ง์ ํ์ธํ ์ ์์ผ๋๊น
๊ตณ์ด ํ ์คํธ๊น์ง ํด์ผ ํ ๊น?"
์ด๋ ๊ฒ ์๊ฐํ๋ ๋ถ๋ค๋ ๋ง์ง๋ง,
์ฌ์ค์ ์คํ๋ ค ๊ทธ ๋ฐ๋์ด๋ค.
๐ก ํ ์คํธ ์ฝ๋๊ฐ ์ค์ํ ์ด์
โ ๋ฆฌํฉํ ๋ง ์ ๊ธฐ๋ฅ์ด ๊นจ์ง์ง ์์๋์ง ๋น ๋ฅด๊ฒ ํ์ธํ ์ ์๋ค.
โ ํ์ ์ค ๋ค๋ฅธ ๊ฐ๋ฐ์๊ฐ ๋ณ๊ฒฝํ ์ฝ๋๊ฐ ๋ด ๊ธฐ๋ฅ์ ์ํฅ์ ์ฃผ๋์ง ๊ฒ์ฆํ ์ ์๋ค.
โ CI / CD ์๋ํ ํ์ดํ๋ผ์ธ์์ ์์ ์ ์ธ ๋ฐฐํฌ๋ฅผ ๋์์ค๋ค.
โ ๋ฒ๊ทธ๋ฅผ ์กฐ๊ธฐ์ ๋ฐ๊ฒฌํ๊ณ ์ ์ง๋ณด์์ฑ์ ๋์ผ ์ ์๋ค.
์ฆ, ํ ์คํธ๋ ํ๋ก ํธ์๋ ๊ฐ๋ฐ์์๋
์ ๋ขฐ์ฑ ์๋ ์ฝ๋ ์์ฑ๊ณผ ์ ์ง๋ณด์๋ฅผ ์ํด
๊ผญ ํ์ํ ๊ณผ์ ์ด๋ค.
๐งช ํ๋ก ํธ์๋ ํ ์คํธ์ ์ข ๋ฅ
| ํ ์คํธ ์ ํ | ์ค๋ช | ์์ |
| Unit Test | ํ๋์ ์ปดํฌ๋ํธ ํน์ ํจ์ ๋จ์ ํ ์คํธ | ๋ฒํผ ํด๋ฆญ ์ ์ฝ๋ฐฑ์ด ํธ์ถ๋๋์ง |
| Integration Test | ์ฌ๋ฌ ์ปดํฌ๋ํธ์ ์ํธ์์ฉ ํ ์คํธ | ๋ก๊ทธ์ธ ํ์ด์ง์์ ์ ๋ ฅ → ๋ฒํผ ํด๋ฆญ → ์ด๋ |
| E2E Test | ์ค์ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์ ์ ์ ํ๋ก์ฐ ํ ์คํธ | Cypress, Playwright ์ฌ์ฉ |
โ๏ธ Jest + Testing Library ์ค์
pnpm install --save-dev jest @types/jest ts-jest
pnpm install --save-dev @testing-library/react @testing-library/jest-dom
jest ๐๐ป ํ ์คํธ๋ฅผ ์คํํด์ฃผ๋ ํ ์คํธ ๋ฌ๋ ์ญํ ์ ํ๋ค.
@testing-library/react ๐๐ป DOM ์ค์ฌ์ ํ ์คํธ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํด์ค๋ค.
@testing-library/jest-dom ๐๐ป toBeInTheDocument() ๋ฑ ์ถ๊ฐ์ ์ธ matcher๋ค์ ์ ๊ณตํด์ค๋ค.
๐ก ์ด๋, jest.config.ts ํน์ jest.config.js ๋ฑ์ ์ค์ ํ์ผ๋ ํจ๊ป ์์ฑํด์ค์ผ ํ๋ค.
๐งพ ๊ธฐ๋ณธ ํ ์คํธ ์์ (App ์ปดํฌ๋ํธ ๋ผ์ฐํ ํ ์คํธ)
import { render, screen } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import App from '../App';
jest.mock('@/pages/main/MainPage', () => {
const MainPage = () => <div>Main Page</div>;
return { __esModule: true, default: MainPage };
});
jest.mock('@/pages/login/LoginPage', () => {
const LoginPage = () => <div>Login Page</div>;
return { __esModule: true, default: LoginPage };
});
describe('App routing', () => {
it('renders MainPage for default route "/"', () => {
render(
<MemoryRouter initialEntries={['/']}>
<App />
</MemoryRouter>,
);
expect(screen.getByText('Main Page')).toBeInTheDocument();
});
it('renders LoginPage for route "/login"', () => {
render(
<MemoryRouter initialEntries={['/login']}>
<App />
</MemoryRouter>,
);
expect(screen.getByText('Login Page')).toBeInTheDocument();
});
});
๐ ํฌ์ธํธ ์ค๋ช
MemoryRouter ๐๐ป ํ ์คํธ์ฉ ๋ผ์ฐํฐ๋ก ์ค์ ๋ธ๋ผ์ฐ์ ์์ด ๋ผ์ฐํ ์ ์๋ฎฌ๋ ์ด์ ํ ์ ์๋๋ก ํด์ค๋ค.
jest.mock ๐๐ป ํ ์คํธ์ ํ์ํ ์ปดํฌ๋ํธ๋ง ๊ฐ์ง๋ก ๋ง๋ค์ด์ ๋ ๋ฆฝ์ฑ์ ํ๋ณดํด์ค๋ค.
screen.getByText() ๐๐ป ์ค์ UI์์ ํน์ ์์๊ฐ ๋ณด์ด๋์ง ๊ฒ์ฌํด์ค๋ค.
expect(...).toBeInTheDocument() ๐๐ป ํด๋น ์์๊ฐ DOM์ ์กด์ฌํ๋์ง ํ์ธํด์ค๋ค.
๐งฏ CI์์ ํ ์คํธ๊น์ง ๋๋ฆฌ๊ณ ์ถ๋ค๋ฉด
- name: Run Test with Coverage
run: pnpm run test -- --coverage --ci
์ ์ฝ๋๋ฅผ ci.yaml ์คํฌ๋ฆฝํธ์ ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.
๐ก ํน์ ์์ง test ํ์ผ์ ์์ฑํ์ง ์์๋ค๋ฉด,
์๋์ ๊ฐ์ด --passWithNoTests ์ต์ ์ ์ฌ์ฉํด์ ์คํจํ์ง ์๋๋ก ์ฒ๋ฆฌํด์ค ์ ์๋ค.
- name: Run Test with Coverage
run: pnpm run test -- --coverage --ci --passWithNoTests
'๐ป๊ณต๋ถ ๊ธฐ๋ก > ๐ Frontend' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
| [Frontend] ์ปดํฌ๋ํธ๋ (1) | 2025.06.16 |
|---|---|
| [Frontend] Mock์ด๋ (0) | 2025.06.14 |
| [Frontend] prettier ์ ์ฉํ๊ธฐ (3) | 2025.06.14 |
| [Frontend] ํจํค์ง ๋งค๋์ ์ ๋์ ๊ณผ์ (2) | 2025.06.04 |
| [Frontend] ํจํค์ง ๋งค๋์ (1) | 2025.06.03 |