8.8 KiB
8.8 KiB
自动化测试指南
本项目包含完整的自动化测试套件,包括单元测试和端到端(E2E)测试。
📁 测试文件结构
backend/services/identity-service/
├── src/
│ ├── domain/
│ │ └── value-objects/
│ │ ├── mnemonic.vo.spec.ts # 助记词单元测试
│ │ └── phone-number.vo.spec.ts # 手机号单元测试
│ └── infrastructure/
│ └── external/
│ └── blockchain/
│ └── wallet-generator.service.spec.ts # 钱包生成服务测试
└── test/
├── app.e2e-spec.ts # 完整的 E2E 测试套件
└── jest-e2e.json # E2E 测试配置
🧪 测试类型
1. 单元测试(Unit Tests)
测试单个组件、类或函数的功能。
位置: 与源代码文件同目录,文件名以 .spec.ts 结尾
已包含的单元测试:
- ✅
mnemonic.vo.spec.ts- 助记词值对象测试 - ✅
phone-number.vo.spec.ts- 手机号值对象测试 - ✅
wallet-generator.service.spec.ts- 钱包生成服务测试 - ✅
user-account.aggregate.spec.ts- 用户账户聚合根测试(已存在)
2. 端到端测试(E2E Tests)
测试完整的用户场景和 API 端点。
位置: test/ 目录
已包含的 E2E 测试:
- ✅
app.e2e-spec.ts- 完整的 API 测试套件(8个测试场景,40+测试用例)
🚀 运行测试
运行所有单元测试
npm test
运行单元测试(监听模式)
npm run test:watch
运行单元测试并生成覆盖率报告
npm run test:cov
运行 E2E 测试
npm run test:e2e
运行特定测试文件
# 运行助记词测试
npm test -- mnemonic.vo.spec
# 运行钱包生成服务测试
npm test -- wallet-generator.service.spec
# 运行手机号测试
npm test -- phone-number.vo.spec
调试测试
npm run test:debug
然后在 Chrome 中打开 chrome://inspect
📊 E2E 测试覆盖的场景
场景 1: 用户注册和账户创建
- ✅ 自动创建账户
- ✅ 验证钱包地址格式
- ✅ 参数验证
场景 2: 用户资料管理
- ✅ 获取个人资料
- ✅ 更新个人资料
- ✅ 认证验证
场景 3: 设备管理
- ✅ 获取设备列表
- ✅ 添加新设备
- ✅ 移除设备
- ✅ 设备数量限制(最多5个)
场景 4: Token 管理
- ✅ 刷新 Token
- ✅ Token 验证
- ✅ 无效 Token 拒绝
场景 5: 推荐系统
- ✅ 根据推荐码查询用户
- ✅ 使用推荐码注册
场景 6: KYC 认证
- ✅ 提交 KYC
- ✅ 身份证号验证
场景 7: 助记词恢复
- ✅ 正确助记词恢复
- ✅ 错误助记词拒绝
- ✅ 账户序列号验证
场景 8: 数据验证
- ✅ 手机号格式验证
- ✅ 各种边界条件测试
🎯 单元测试覆盖的功能
Mnemonic 值对象测试
- ✅ 生成 12 个单词助记词
- ✅ 转换为 seed
- ✅ 验证助记词格式
- ✅ 助记词相等性比较
- ✅ 拒绝无效助记词
PhoneNumber 值对象测试
- ✅ 验证有效的中国手机号
- ✅ 拒绝无效格式
- ✅ 手机号掩码(138****8000)
- ✅ 手机号相等性比较
WalletGenerator 服务测试
- ✅ 生成完整钱包系统
- ✅ 三条链地址生成(KAVA/DST/BSC)
- ✅ 地址格式验证
- ✅ 助记词恢复
- ✅ 助记词验证
- ✅ 加密和解密
- ✅ 密钥派生
📈 查看测试覆盖率
运行覆盖率测试:
npm run test:cov
覆盖率报告会生成在 coverage/ 目录:
# 在浏览器中查看 HTML 报告
open coverage/lcov-report/index.html # macOS
start coverage/lcov-report/index.html # Windows
🔧 测试配置
单元测试配置 (package.json)
{
"jest": {
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"moduleNameMapper": {
"^@/(.*)$": "<rootDir>/$1"
}
}
}
E2E 测试配置 (test/jest-e2e.json)
{
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": ".",
"testEnvironment": "node",
"testRegex": ".e2e-spec.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
}
}
🧩 编写新的测试
添加单元测试
在源文件同目录创建 .spec.ts 文件:
// src/domain/value-objects/my-value.vo.spec.ts
import { MyValue } from './my-value.vo';
describe('MyValue', () => {
describe('create', () => {
it('should create valid value', () => {
const value = MyValue.create('test');
expect(value).toBeDefined();
});
});
});
添加 E2E 测试
在 test/ 目录添加新的测试场景:
// test/new-feature.e2e-spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '@/app.module';
describe('New Feature E2E Tests', () => {
let app: INestApplication;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
await app.init();
});
afterAll(async () => {
await app.close();
});
it('should test new feature', async () => {
const response = await request(app.getHttpServer())
.get('/api/v1/new-endpoint')
.expect(200);
expect(response.body).toBeDefined();
});
});
🎬 CI/CD 集成
GitHub Actions 示例
创建 .github/workflows/test.yml:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16
env:
POSTGRES_PASSWORD: postgres
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Generate Prisma Client
run: npm run prisma:generate
- name: Run unit tests
run: npm test
- name: Run E2E tests
run: npm run test:e2e
env:
DATABASE_URL: postgresql://postgres:postgres@localhost:5432/test
- name: Upload coverage
uses: codecov/codecov-action@v3
📋 测试最佳实践
1. 测试命名
使用清晰的描述性名称:
// ✅ 好的命名
it('应该拒绝无效的手机号格式', () => {});
// ❌ 不好的命名
it('test1', () => {});
2. AAA 模式
遵循 Arrange-Act-Assert 模式:
it('应该创建用户', () => {
// Arrange - 准备测试数据
const userData = { name: 'Test' };
// Act - 执行操作
const user = createUser(userData);
// Assert - 验证结果
expect(user.name).toBe('Test');
});
3. 独立性
每个测试应该独立运行:
// ✅ 好的做法
beforeEach(() => {
// 为每个测试准备新的数据
testData = createFreshData();
});
// ❌ 不好的做法
let sharedData;
it('test1', () => {
sharedData = modify(sharedData); // 影响其他测试
});
4. 清理
在测试后清理资源:
afterEach(async () => {
await cleanupTestData();
});
afterAll(async () => {
await app.close();
});
🐛 调试失败的测试
1. 查看详细输出
npm test -- --verbose
2. 运行单个测试
npm test -- --testNamePattern="应该创建用户"
3. 使用 console.log
it('debug test', () => {
const result = someFunction();
console.log('Result:', result); // 调试输出
expect(result).toBe(expected);
});
4. 使用 Node 调试器
npm run test:debug
在 Chrome 打开 chrome://inspect,点击 "inspect"
📚 相关资源
🎯 测试目标
当前覆盖率目标
- 单元测试覆盖率:> 80%
- E2E 测试覆盖率:> 70%
- 关键路径覆盖率:100%
持续改进
- 为新功能添加测试
- 为 bug 修复添加回归测试
- 定期审查和更新测试
- 移除过时的测试
🚀 快速开始
# 1. 安装依赖
npm install
# 2. 安装 supertest(E2E 测试依赖)
npm install --save-dev supertest @types/supertest
# 3. 运行所有测试
npm test
# 4. 运行 E2E 测试
npm run test:e2e
# 5. 查看覆盖率
npm run test:cov
祝测试愉快!🎉