testing-strategies by supercent-io/skills-template
npx skills add https://github.com/supercent-io/skills-template --skill testing-strategies /\
/E2E\ ← 少量(慢,昂贵)
/______\
/ \
/Integration\ ← 中等数量
/____________\
/ \
/ Unit Tests \ ← 大量(快,便宜)
/________________\
比例指南:
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');
});
});
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
模拟策略:
// 模拟外部依赖
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)
});
});
});
API 端点测试:
describe('POST /api/users', () => {
beforeEach(async () => {
await db.user.deleteMany(); // 清理数据库
});
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'
});
// 验证数据确实保存到了数据库
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);
});
});
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();
});
});
红-绿-重构循环:
// 1. 红:编写一个失败的测试
describe('isPalindrome', () => {
it('should return true for palindrome', () => {
expect(isPalindrome('racecar')).toBe(true);
});
});
// 2. 绿:编写最少代码使测试通过
function isPalindrome(str: string): boolean {
return str === str.split('').reverse().join('');
}
// 3. 重构:改进代码
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);
});
## 测试策略
### 覆盖率目标
- 单元测试:80%
- 集成测试:60%
- E2E 测试:关键用户流程
### 测试执行
- 单元测试:每次提交(本地 + CI)
- 集成测试:每次 PR
- E2E 测试:部署前
### 工具
- 单元测试:Jest
- 集成测试:Supertest
- E2E 测试:Playwright
- 覆盖率:Istanbul/nyc
### CI/CD 集成
- GitHub Actions:在 PR 上运行所有测试
- 如果覆盖率 < 80% 则构建失败
- 在预发布环境运行 E2E 测试
#testing #test-strategy #TDD #unit-test #integration-test #E2E #code-quality
每周安装
11.2K
仓库
GitHub Stars
88
首次出现
Jan 24, 2026
安全审计
安装于
codex11.1K
gemini-cli11.1K
opencode11.1K
github-copilot11.1K
cursor11.0K
cline11.0K
/\
/E2E\ ← few (slow, expensive)
/______\
/ \
/Integration\ ← medium
/____________\
/ \
/ Unit Tests \ ← many (fast, inexpensive)
/________________\
Ratio guide :
Given-When-Then pattern :
describe('calculateDiscount', () => {
it('should apply 10% discount for orders over $100', () => {
// Given: setup
const order = { total: 150, customerId: '123' };
// When: perform action
const discount = calculateDiscount(order);
// Then: verify result
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 strategy :
// Mock external dependencies
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)
});
});
});
API endpoint tests :
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'
});
// Verify it was actually saved to the DB
const user = await db.user.findUnique({ where: { email: 'test@example.com' } });
expect(user).toBeTruthy();
});
it('should reject duplicate email', async () => {
// Create first user
await request(app)
.post('/api/users')
.send({ email: 'test@example.com', username: 'user1', password: 'Pass123!' });
// Attempt duplicate
const response = await request(app)
.post('/api/users')
.send({ email: 'test@example.com', username: 'user2', password: 'Pass123!' });
expect(response.status).toBe(409);
});
});
import { test, expect } from '@playwright/test';
test.describe('User Registration Flow', () => {
test('should complete full registration process', async ({ page }) => {
// 1. Visit homepage
await page.goto('http://localhost:3000');
// 2. Click Sign Up button
await page.click('text=Sign Up');
// 3. Fill out form
await page.fill('input[name="email"]', 'test@example.com');
await page.fill('input[name="username"]', 'testuser');
await page.fill('input[name="password"]', 'Password123!');
// 4. Submit
await page.click('button[type="submit"]');
// 5. Confirm success message
await expect(page.locator('text=Welcome')).toBeVisible();
// 6. Confirm redirect to dashboard
await expect(page).toHaveURL('http://localhost:3000/dashboard');
// 7. Confirm user info is displayed
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();
});
});
Red-Green-Refactor Cycle :
// 1. RED: write a failing test
describe('isPalindrome', () => {
it('should return true for palindrome', () => {
expect(isPalindrome('racecar')).toBe(true);
});
});
// 2. GREEN: minimal code to pass the test
function isPalindrome(str: string): boolean {
return str === str.split('').reverse().join('');
}
// 3. REFACTOR: improve the code
function isPalindrome(str: string): boolean {
const cleaned = str.toLowerCase().replace(/[^a-z0-9]/g, '');
return cleaned === cleaned.split('').reverse().join('');
}
// 4. Additional test cases
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);
});
## 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
#testing #test-strategy #TDD #unit-test #integration-test #E2E #code-quality
Weekly Installs
11.2K
Repository
GitHub Stars
88
First Seen
Jan 24, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex11.1K
gemini-cli11.1K
opencode11.1K
github-copilot11.1K
cursor11.0K
cline11.0K
Skills CLI 使用指南:AI Agent 技能包管理器安装与管理教程
27,400 周安装
Power BI 数据模型设计审查指南 - 架构、性能与可维护性全面评估
7,500 周安装
PHP MCP服务器生成器 - 快速创建生产级MCP服务器项目 | PHP 8.2+
7,500 周安装
IMAP/SMTP 邮箱工具:支持 Gmail、Outlook、163 等服务器的邮件收发与管理
214 周安装
GitHub Copilot AI模型推荐工具:根据任务复杂度自动选择最佳AI模型
7,500 周安装
文本转语音工具 - 使用 inference.sh CLI 实现高品质AI语音合成,支持多种模型和语言
7,600 周安装
GitHub Copilot 技能推荐工具 - 自动分析仓库并推荐相关AI技能
7,600 周安装