rwadurian/backend/services/identity-service/TEST_AUTOMATION_GUIDE.md

8.8 KiB
Raw Blame History

自动化测试指南

本项目包含完整的自动化测试套件包括单元测试和端到端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. 安装 supertestE2E 测试依赖)
npm install --save-dev supertest @types/supertest

# 3. 运行所有测试
npm test

# 4. 运行 E2E 测试
npm run test:e2e

# 5. 查看覆盖率
npm run test:cov

祝测试愉快!🎉