Claude-skill-registry frontend-tdd
Frontend TDD Agent. React/Next.js 기반 TDD 테스트 작성 및 구현을 담당합니다. 테스트 먼저 작성 후 구현하는 Red-Green-Refactor 사이클을 따릅니다.
install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/frontend-tdd" ~/.claude/skills/majiayu000-claude-skill-registry-frontend-tdd && rm -rf "$T"
manifest:
skills/data/frontend-tdd/SKILL.mdsource content
Frontend TDD Agent
역할
TDD(Test-Driven Development) 방식으로 Frontend 코드를 개발합니다. 테스트를 먼저 작성하고, 테스트를 통과하는 최소한의 코드를 구현합니다.
TDD 사이클
┌─────────────────────────────────────────────────────────────────┐ │ 1. RED (실패하는 테스트) │ │ - 테스트 케이스 작성 │ │ - 테스트 실행 → 실패 확인 │ └─────────────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 2. GREEN (테스트 통과) │ │ - 최소한의 코드 작성 │ │ - 테스트 실행 → 통과 확인 │ └─────────────────────────────┬───────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────────┐ │ 3. REFACTOR (리팩토링) │ │ - 코드 개선 │ │ - 테스트 실행 → 여전히 통과 확인 │ └─────────────────────────────────────────────────────────────────┘
테스트 스택
- Test Runner: Jest / Vitest
- Component Testing: React Testing Library
- E2E Testing: Playwright
- Mocking: MSW (Mock Service Worker)
- Coverage: Istanbul / c8
테스트 유형
1. 컴포넌트 테스트
import { render, screen, fireEvent } from '@testing-library/react'; import { UserProfile } from './UserProfile'; describe('UserProfile', () => { // 렌더링 테스트 it('should render user name', () => { render(<UserProfile user={{ name: 'John', email: 'john@test.com' }} />); expect(screen.getByText('John')).toBeInTheDocument(); }); // 인터랙션 테스트 it('should call onEdit when edit button clicked', () => { const onEdit = jest.fn(); render(<UserProfile user={{ name: 'John' }} onEdit={onEdit} />); fireEvent.click(screen.getByRole('button', { name: /edit/i })); expect(onEdit).toHaveBeenCalledTimes(1); }); // 상태 테스트 it('should show loading state', () => { render(<UserProfile isLoading />); expect(screen.getByTestId('loading-spinner')).toBeInTheDocument(); }); // 에러 상태 테스트 it('should show error message when error occurs', () => { render(<UserProfile error="Failed to load" />); expect(screen.getByRole('alert')).toHaveTextContent('Failed to load'); }); });
2. Hook 테스트
import { renderHook, act } from '@testing-library/react'; import { useCounter } from './useCounter'; describe('useCounter', () => { it('should initialize with default value', () => { const { result } = renderHook(() => useCounter(0)); expect(result.current.count).toBe(0); }); it('should increment count', () => { const { result } = renderHook(() => useCounter(0)); act(() => { result.current.increment(); }); expect(result.current.count).toBe(1); }); it('should decrement count', () => { const { result } = renderHook(() => useCounter(10)); act(() => { result.current.decrement(); }); expect(result.current.count).toBe(9); }); });
3. API 통합 테스트 (MSW)
import { rest } from 'msw'; import { setupServer } from 'msw/node'; import { render, screen, waitFor } from '@testing-library/react'; import { UserList } from './UserList'; const server = setupServer( rest.get('/api/users', (req, res, ctx) => { return res( ctx.json([ { id: 1, name: 'John' }, { id: 2, name: 'Jane' }, ]) ); }) ); beforeAll(() => server.listen()); afterEach(() => server.resetHandlers()); afterAll(() => server.close()); describe('UserList', () => { it('should fetch and display users', async () => { render(<UserList />); await waitFor(() => { expect(screen.getByText('John')).toBeInTheDocument(); expect(screen.getByText('Jane')).toBeInTheDocument(); }); }); it('should handle API error', async () => { server.use( rest.get('/api/users', (req, res, ctx) => { return res(ctx.status(500)); }) ); render(<UserList />); await waitFor(() => { expect(screen.getByRole('alert')).toBeInTheDocument(); }); }); });
테스트 명령어
# 전체 테스트 실행 npm run test # Watch 모드 npm run test:watch # 특정 파일 테스트 npm run test -- UserProfile # 커버리지 리포트 npm run test:coverage # E2E 테스트 (Playwright) npm run test:e2e
테스트 패턴
AAA 패턴 (Arrange-Act-Assert)
it('should do something', () => { // Arrange - 준비 const user = { name: 'John' }; // Act - 실행 render(<Component user={user} />); // Assert - 검증 expect(screen.getByText('John')).toBeInTheDocument(); });
Given-When-Then 패턴
describe('Login Form', () => { describe('given valid credentials', () => { describe('when user submits form', () => { it('then should redirect to dashboard', async () => { // test code }); }); }); });
테스트 커버리지 목표
| 유형 | 목표 |
|---|---|
| 전체 | > 80% |
| 컴포넌트 | > 90% |
| Hooks | > 95% |
| Utils | > 95% |
TDD 체크리스트
테스트 작성 전
- 요구사항이 명확한가?
- 테스트할 동작을 정의했는가?
- 엣지 케이스를 식별했는가?
테스트 작성 시
- 테스트명이 동작을 설명하는가?
- 하나의 테스트는 하나만 검증하는가?
- 테스트가 실패하는가? (RED)
구현 시
- 최소한의 코드로 구현했는가?
- 테스트가 통과하는가? (GREEN)
리팩토링 시
- 중복 코드를 제거했는가?
- 테스트가 여전히 통과하는가?
- 코드가 읽기 쉬운가?
산출물 위치
- 테스트 코드:
,src/**/*.test.tsxsrc/**/*.spec.tsx - 테스트 유틸:
src/test/utils.tsx - MSW 핸들러:
src/mocks/handlers.ts