测试最佳实践
测试设计原则
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
持续改进
定期审查测试
- 删除过时的测试
- 优化慢测试
- 提高测试覆盖率
- 更新测试文档
收集反馈
- 记录测试失败原因
- 分析测试通过率趋势
- 优化测试稳定性