Skip to content

测试最佳实践

测试设计原则

1. 独立可重复

每个测试应该:

  • 不依赖其他测试的执行顺序
  • 可以独立运行
  • 每次运行产生相同的结果

2. 清晰明确

测试命名应该清晰表达意图:

php
// ✅ 好
public function testMarketingCopyGeneration(): void {}

// ❌ 不好
public function testCopy1(): void {}

3. 单一职责

每个测试只验证一个功能点:

php
// ✅ 好
public function testMarketingCopyGeneration(): void {}
public function testReviewReplyGeneration(): void {}

// ❌ 不好
public function testAllCopyFeatures(): void {}

测试分类

按执行速度分类

快速测试(Unit Tests)

  • 执行时间:< 1秒
  • 不依赖外部服务
  • 可频繁运行

中等速度测试(Integration Tests)

  • 执行时间:1-10秒
  • 依赖数据库等本地服务
  • 每次提交前运行

慢速测试(E2E Tests)

  • 执行时间:> 10秒
  • 依赖外部 API 服务
  • 发布前或定时运行

按功能分类

tests/
├── Unit/              # 单元测试
│   ├── ServiceTest.php
│   └── UtilTest.php
├── Integration/       # 集成测试
│   ├── DatabaseTest.php
│   └── ApiTest.php
└── E2E/              # 端到端测试
    ├── AigcImagePipelineTest.php
    ├── AigcVideoPipelineTest.php
    └── AigcCopyPipelineTest.php

测试数据管理

使用数据提供器

php
/**
 * @dataProvider platformStyleProvider
 */
public function testCopyGeneration(string $platform, string $style): void
{
    // 测试逻辑
}

public static function platformStyleProvider(): array
{
    return [
        ['xiaohongshu', 'literary'],
        ['moments', 'promotion'],
        ['wechat', 'guide'],
    ];
}

Fixture 管理

php
protected function setUp(): void
{
    parent::setUp();
    // 准备测试数据
}

protected function tearDown(): void
{
    // 清理测试数据
    parent::tearDown();
}

断言最佳实践

明确的断言信息

php
// ✅ 好
$this->assertNotEmpty($content, 'Generated content should not be empty');

// ❌ 不好
$this->assertNotEmpty($content);

避免过度断言

php
// ✅ 好 - 只测试必要的
$this->assertEquals(0, $body['code']);
$this->assertNotEmpty($body['data']['content']);

// ❌ 不好 - 测试太多细节
$this->assertEquals(0, $body['code']);
$this->assertEquals('success', $body['message']);
$this->assertArrayHasKey('content', $body['data']);
$this->assertIsString($body['data']['content']);
$this->assertGreaterThan(10, strlen($body['data']['content']));

Mock 策略

何时使用 Mock

场景是否 Mock原因
外部 API 调用✅ 是避免真实调用费用
数据库查询❌ 否测试真实集成
文件系统⚠️ 可选视情况而定
时间相关✅ 是确保测试可重复

Mock 示例

php
// 使用 Mock 避免真实 API 调用
$llmService = $this->createMock(UnifiedLlmService::class);
$llmService->method('generate')
    ->willReturn([
        'messages' => [
            ['content' => 'Mocked response']
        ]
    ]);

性能优化

测试分组

php
/**
 * @group slow
 * @group aigc-e2e
 */
public function testSlowAigcPipeline(): void {}
bash
# 跳过慢速测试
vendor/bin/phpunit --exclude-group slow

# 只运行快速测试
vendor/bin/phpunit --group unit

测试并行化

bash
# 使用 paratest
composer require --dev brianium/paratest
vendor/bin/paratest -p4

调试技巧

输出调试信息

php
public function testSomething(): void
{
    $result = $this->apiCall();
    
    // 调试输出
    var_dump($result);
    
    // 或使用 PHPUnit 的输出
    fwrite(STDERR, print_r($result, true));
}

停止在第一个失败

bash
vendor/bin/phpunit --stop-on-failure

只运行失败的测试

bash
vendor/bin/phpunit --order-by=defects

文档化测试

测试注释

php
/**
 * 测试营销文案生成功能
 * 
 * 测试流程:
 * 1. 构建测试参数
 * 2. 发送 API 请求
 * 3. 验证响应状态码
 * 4. 验证返回的文案内容
 * 
 * @group aigc-e2e
 */
public function testMarketingCopyGeneration(): void {}

测试报告

bash
# 生成 HTML 报告
vendor/bin/phpunit --coverage-html coverage/

# 生成 JUnit XML
vendor/bin/phpunit --log-junit results.xml

安全注意事项

不要提交敏感信息

php
// ✅ 好 - 使用环境变量
$apiKey = getenv('VOLCENGINE_API_KEY');

// ❌ 不好 - 硬编码密钥
$apiKey = 'sk-actual-secret-key-123456';

测试环境隔离

确保测试使用独立的:

  • 数据库
  • API 密钥
  • 存储 bucket

持续改进

定期审查测试

  • 删除过时的测试
  • 优化慢测试
  • 提高测试覆盖率
  • 更新测试文档

收集反馈

  • 记录测试失败原因
  • 分析测试通过率趋势
  • 优化测试稳定性

相关文档