第 2.8 章:API 测试
学习时间: 1 小时
1. 为什么需要测试?
想象一下您建造了一艘宇宙飞船。在将其送往火星之前,您会在地球上进行数千次检查。编程中的测试也是如此。它们:
- 提供信心: 您可以更改代码,如果测试通过,就意味着您没有破坏任何东西。
- 节省时间: 无需在每次更改后手动在 Postman 中“点击”所有内容,您只需运行一个命令,它就会在几秒钟内为您检查所有内容。
- 充当文档: 良好的测试会显示您的 API 应该如何工作。
2. 设置测试“实验室”
Laravel 让测试设置变得异常简单。默认情况下,它使用独立的配置,以避免影响您的主数据库。
测试数据库:
默认情况下,Laravel 使用内存中的数据库 (:memory:
)。这是最快的方式,因为无需写入磁盘。数据库在测试前创建,并在测试后销毁。我们甚至不需要为此进行任何配置!
创建测试文件: 让我们创建一个专门用于行星相关测试的文件。
此命令将创建文件 tests/Feature/PlanetApiTest.php
。单词 Feature
意味着我们将测试整体功能(例如,“用户能否创建行星?”),而不是单个小类。
3. 测试剖析:准备、执行、验证
打开 tests/Feature/PlanetApiTest.php
。我们将在其中编写第一个测试。一个好的测试总是由三部分组成(Arrange, Act, Assert)。
<?php
namespace Tests\Feature;
use App\Models\Planet; // 不要忘记导入模型
use Illuminate\Foundation\Testing\RefreshDatabase; // 最重要的工具!
use Tests\TestCase;
class PlanetApiTest extends TestCase
{
// 这个 trait “神奇地”清除并重新创建
// 我们的测试数据库在每个测试之前。
// 这确保了测试之间互不影响。
use RefreshDatabase;
/**
* 测试:获取行星列表的端点是否正常工作。
* 测试名称应该有意义!
*/
public function test_can_get_all_planets(): void
{
// 1. 准备 (Arrange)
// 在我们的测试数据库中创建 3 个模拟行星
// 使用我们之前创建的工厂。
Planet::factory()->count(3)->create();
// 2. 执行 (Act)
// 模拟对我们 API 的真实 GET 请求。
$response = $this->getJson('/api/planets');
// 3. 验证 (Assert)
// 检查一切是否按预期进行。
$response->assertStatus(200); // 期望服务器响应 "200 OK"
$response->assertJsonCount(3); // 期望响应中正好有 3 个行星
}
}
use RefreshDatabase
:这个 trait 是您最好的朋友。它确保每个测试都从“干净的画布”开始,拥有一个空的数据库。Planet::factory()
:工厂非常适合创建测试数据。$this->getJson()
:这是 Laravel 用于在测试内部发送 API 请求的特殊方法。assert...()
:这些是“断言”或“检查”。如果其中任何一个没有执行,测试就会失败。
4. 测试基本操作 (CRUD)
让我们编写用于创建、更新和删除行星的测试。
A. 创建行星测试 (POST)
<?php
public function test_can_create_a_planet(): void
{
// 1. 准备:准备新行星的数据
$planetData = [
'name' => 'Kepler-186f',
'description' => 'Первая экзопланета размером с Землю в обитаемой зоне.',
'size_km' => 14000,
'solar_system' => 'Kepler-186'
];
// 2. 执行:发送带有数据的 POST 请求
$response = $this->postJson('/api/planets', $planetData);
// 3. 验证
$response->assertStatus(201); // 期望状态 "201 Created"
$response->assertJsonFragment(['name' => 'Kepler-186f']); // 检查响应中是否存在创建的名称
// 最重要的检查:数据是否真的进入了数据库?
$this->assertDatabaseHas('planets', [
'name' => 'Kepler-186f'
]);
}
B. 删除行星测试 (DELETE)
<?php
public function test_can_delete_a_planet(): void
{
// 1. 准备:创建要删除的行星
$planet = Planet::factory()->create();
// 2. 执行:发送 DELETE 请求
$response = $this->deleteJson("/api/planets/{$planet->id}");
// 3. 验证
$response->assertStatus(204); // 期望 "204 No Content" - 成功删除
// 检查记录是否确实从数据库中消失
$this->assertDatabaseMissing('planets', [
'id' => $planet->id
]);
}
5. 测试“糟糕”场景
测试成功案例是好事。但更重要的是测试错误!
A. 验证错误测试
<?php
public function test_creation_fails_with_invalid_data(): void
{
// 2. 执行:发送明显不正确的数据
$response = $this->postJson('/api/planets', ['name' => '']); // 空名称
// 3. 验证
$response->assertStatus(422); // 期望 "422 Unprocessable Entity"
$response->assertJsonValidationErrors('name'); // 期望错误出现在 'name' 字段中
}
B. “未找到”测试 (404)
<?php
public function test_returns_404_for_non_existent_planet(): void
{
// 2. 执行:请求一个不存在 ID 的行星
$response = $this->getJson('/api/planets/99999');
// 3. 验证
$response->assertStatus(404); // 期望 "404 Not Found"
}
6. 运行测试
现在,测试已编写完成,运行它们非常简单。在终端中执行:
Laravel 将找到您所有的测试并逐一执行。如果一切顺利,您将看到绿色的输出。如果某个测试失败,您将看到红色的输出以及详细的错误描述,这将使您能够快速修复它。
要仅运行一个特定文件:
8. 代码覆盖率 (Code Coverage)
步骤 1:安装 Xdebug
为了收集代码覆盖率信息,需要 PHP 扩展 — Xdebug。
将您的
php -i
发送到向导并按照说明进行操作。
步骤 2:配置 phpunit.xml
<phpunit ... >
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./app</directory>
</include>
</coverage>
</phpunit>
步骤 3:运行并生成报告
报告:在浏览器中打开coverage/index.html
9. 与 Postman 集成
通过 Newman 自动化:
- 将 Postman 集合导出到
tests/Postman/SpaceApi.postman_collection.json
- 安装 Newman:
- 将脚本添加到
composer.json
: - 运行:
巩固测验
🚀 本章总结: 您已完成完整的飞行前测试周期!现在您的API可以:
- ✅ 轻松配置测试环境
- 🛡️ 按照“准备-操作-验证”原则编写测试
- 📊 测试成功场景 (CRUD) 和错误 (验证, 404)
- 🔁 使用一条命令运行测试,并对代码充满信心
宇宙飞船已准备就绪,可以发射! 您已完成Laravel API创建部分。
📌 最终检查:
- 运行
php artisan test
- 确保所有测试通过(绿灯!)
- 检查覆盖率报告
⚠️ 如果测试失败:
- 通过 Postman 检查 API 的运行情况
- 确保测试数据库已配置
- 使用
dd($response->content())
进行调试
恭喜您完成第2章! 您不仅创建了一个 API,还创建了一个可靠且经过验证的“宇宙飞船”,可用于未来的任务。
🌌 后续步骤:
- 配置认证 (Sanctum)
- 使用 Swagger 文档化 API
- 在服务器上部署 (Forge, VPS)
- 使用 Vue/React 编写前端
祝您的太空任务发射成功!在下一章中,我们将从头开始编写 API 🚀