testing-strategies
54
总安装量
55
周安装量
#4004
全站排名
安装命令
npx skills add https://github.com/supercent-io/skills-template --skill testing-strategies
Agent 安装分布
opencode
46
claude-code
41
gemini-cli
40
github-copilot
33
antigravity
32
Skill 文档
Testing Strategies
When to use this skill
- ì ê· íë¡ì í¸: í ì¤í¸ ì ëµ ì립
- íì§ ë¬¸ì : ë²ê·¸ ë¹ë²í ë°ì
- 리í©í ë§ ì : ìì ë§ êµ¬ì¶
- CI/CD 구ì¶: ìëíë í ì¤í¸
Instructions
Step 1: Test Pyramid ì´í´
/\
/E2E\ â ì ì (ë림, ë¹ì© ëì)
/______\
/ \
/Integration\ â ì¤ê°
/____________\
/ \
/ Unit Tests \ â ë§ì (ë¹ ë¦, ë¹ì© ë®ì)
/________________\
ë¹ì¨ ê°ì´ë:
- Unit: 70%
- Integration: 20%
- E2E: 10%
Step 2: Unit Testing ì ëµ
Given-When-Then í¨í´:
describe('calculateDiscount', () => {
it('should apply 10% discount for orders over $100', () => {
// Given: 주ì´ì§ ìí©
const order = { total: 150, customerId: '123' };
// When: íëì ì¤í
const discount = calculateDiscount(order);
// Then: ê²°ê³¼ ê²ì¦
expect(discount).toBe(15);
});
it('should not apply discount for orders under $100', () => {
const order = { total: 50, customerId: '123' };
const discount = calculateDiscount(order);
expect(discount).toBe(0);
});
it('should throw error for invalid order', () => {
const order = { total: -10, customerId: '123' };
expect(() => calculateDiscount(order)).toThrow('Invalid order');
});
});
Mocking ì ëµ:
// ì¸ë¶ ìì¡´ì± ëª¨í¹
jest.mock('../services/emailService');
import { sendEmail } from '../services/emailService';
describe('UserService', () => {
it('should send welcome email on registration', async () => {
// Arrange
const mockSendEmail = sendEmail as jest.MockedFunction<typeof sendEmail>;
mockSendEmail.mockResolvedValueOnce(true);
// Act
await userService.register({ email: 'test@example.com', password: 'pass' });
// Assert
expect(mockSendEmail).toHaveBeenCalledWith({
to: 'test@example.com',
subject: 'Welcome!',
body: expect.any(String)
});
});
});
Step 3: Integration Testing
API ìëí¬ì¸í¸ í ì¤í¸:
describe('POST /api/users', () => {
beforeEach(async () => {
await db.user.deleteMany(); // Clean DB
});
it('should create user with valid data', async () => {
const response = await request(app)
.post('/api/users')
.send({
email: 'test@example.com',
username: 'testuser',
password: 'Password123!'
});
expect(response.status).toBe(201);
expect(response.body.user).toMatchObject({
email: 'test@example.com',
username: 'testuser'
});
// DBì ì¤ì ë¡ ì ì¥ëìëì§ íì¸
const user = await db.user.findUnique({ where: { email: 'test@example.com' } });
expect(user).toBeTruthy();
});
it('should reject duplicate email', async () => {
// 첫 ë²ì§¸ ì¬ì©ì ìì±
await request(app)
.post('/api/users')
.send({ email: 'test@example.com', username: 'user1', password: 'Pass123!' });
// ì¤ë³µ ìë
const response = await request(app)
.post('/api/users')
.send({ email: 'test@example.com', username: 'user2', password: 'Pass123!' });
expect(response.status).toBe(409);
});
});
Step 4: E2E Testing (Playwright)
import { test, expect } from '@playwright/test';
test.describe('User Registration Flow', () => {
test('should complete full registration process', async ({ page }) => {
// 1. ííì´ì§ 방문
await page.goto('http://localhost:3000');
// 2. íìê°ì
ë²í¼ í´ë¦
await page.click('text=Sign Up');
// 3. í¼ ìì±
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="username"]', 'testuser');
await page.fill('input[name="password"]', 'Password123!');
// 4. ì ì¶
await page.click('button[type="submit"]');
// 5. ì±ê³µ ë©ìì§ íì¸
await expect(page.locator('text=Welcome')).toBeVisible();
// 6. ëìë³´ëë¡ ë¦¬ë¤ì´ë í¸ íì¸
await expect(page).toHaveURL('http://localhost:3000/dashboard');
// 7. ì¬ì©ì ì ë³´ íì íì¸
await expect(page.locator('text=testuser')).toBeVisible();
});
test('should show error for invalid email', async ({ page }) => {
await page.goto('http://localhost:3000/signup');
await page.fill('input[name="email"]', 'invalid-email');
await page.fill('input[name="password"]', 'Password123!');
await page.click('button[type="submit"]');
await expect(page.locator('text=Invalid email')).toBeVisible();
});
});
Step 5: TDD (Test-Driven Development)
Red-Green-Refactor Cycle:
// 1. RED: ì¤í¨íë í
ì¤í¸ ìì±
describe('isPalindrome', () => {
it('should return true for palindrome', () => {
expect(isPalindrome('racecar')).toBe(true);
});
});
// 2. GREEN: í
ì¤í¸ íµê³¼íë ìµì ì½ë
function isPalindrome(str: string): boolean {
return str === str.split('').reverse().join('');
}
// 3. REFACTOR: ì½ë ê°ì
function isPalindrome(str: string): boolean {
const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
return cleaned === cleaned.split('').reverse().join('');
}
// 4. ì¶ê° í
ì¤í¸ ì¼ì´ì¤
it('should ignore case and spaces', () => {
expect(isPalindrome('A man a plan a canal Panama')).toBe(true);
});
it('should return false for non-palindrome', () => {
expect(isPalindrome('hello')).toBe(false);
});
Output format
í ì¤í¸ ì ëµ ë¬¸ì
## Testing Strategy
### Coverage Goals
- Unit Tests: 80%
- Integration Tests: 60%
- E2E Tests: Critical user flows
### Test Execution
- Unit: Every commit (local + CI)
- Integration: Every PR
- E2E: Before deployment
### Tools
- Unit: Jest
- Integration: Supertest
- E2E: Playwright
- Coverage: Istanbul/nyc
### CI/CD Integration
- GitHub Actions: Run all tests on PR
- Fail build if coverage < 80%
- E2E tests on staging environment
Constraints
íì ê·ì¹ (MUST)
- í ì¤í¸ 격리: ê° í ì¤í¸ë ë 립ì
- Fast Feedback: Unit testsë ë¹ ë¥´ê² (<1ë¶)
- Deterministic: ê°ì ì ë ¥ â ê°ì ê²°ê³¼
ê¸ì§ ì¬í (MUST NOT)
- í ì¤í¸ ìì¡´ì±: í ì¤í¸ Aê° í ì¤í¸ Bì ìì¡´ ê¸ì§
- íë¡ëì DB: í ì¤í¸ìì ì¤ì DB ì¬ì© ê¸ì§
- Sleep/Timeout: ìê° ê¸°ë° í ì¤í¸ ì§ì
Best practices
- AAA í¨í´: Arrange-Act-Assert
- í ì¤í¸ ì´ë¦: “should … when …”
- Edge Cases: ê²½ê³ê°, null, ë¹ ê° í ì¤í¸
- Happy Path + Sad Path: ì±ê³µ/ì¤í¨ ìëë¦¬ì¤ ëª¨ë
References
Metadata
ë²ì
- íì¬ ë²ì : 1.0.0
- ìµì¢ ì ë°ì´í¸: 2025-01-01
- í¸í íë«í¼: Claude, ChatGPT, Gemini
ê´ë ¨ ì¤í¬
íê·¸
#testing #test-strategy #TDD #unit-test #integration-test #E2E #code-quality