HTTP 测试
HTTP 测试
简介
Laravel 提供了流畅的 API,用于向应用发起 HTTP 请求并检查响应。例如,下面定义了一个功能测试:
<?php
test('the application returns a successful response', function () {
$response = $this->get('/');
$response->assertStatus(200);
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的测试示例。
*/
public function test_the_application_returns_a_successful_response(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
get 方法会向应用发起 GET 请求,而 assertStatus 方法则断言返回的响应具有给定的 HTTP 状态码。除了这个简单的断言,Laravel 还提供了多种断言,用于检查响应头、内容、JSON 结构等。
发起请求
你可以在测试中调用 get、post、put、patch 或 delete 方法,向应用发起请求。这些方法不会向应用发出「真实的」HTTP 请求,而是在内部模拟整个网络请求。
测试请求方法返回的是 Illuminate\Testing\TestResponse 实例,而非 Illuminate\Http\Response 实例。它提供了多种实用的断言,方便你检查应用的响应:
<?php
test('basic request', function () {
$response = $this->get('/');
$response->assertStatus(200);
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的测试示例。
*/
public function test_a_basic_request(): void
{
$response = $this->get('/');
$response->assertStatus(200);
}
}
通常,每个测试应该只向应用发起一次请求。在同一个测试方法中执行多个请求可能导致意外行为。
[!NOTE]
为了方便测试,运行测试时会自动禁用 CSRF 中间件。
自定义请求头
你可以使用 withHeaders 方法,在请求发送到应用之前自定义请求头。该方法允许你为请求添加任意自定义请求头:
<?php
test('interacting with headers', function () {
$response = $this->withHeaders([
'X-Header' => 'Value',
])->post('/user', ['name' => 'Sally']);
$response->assertStatus(201);
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*/
public function test_interacting_with_headers(): void
{
$response = $this->withHeaders([
'X-Header' => 'Value',
])->post('/user', ['name' => 'Sally']);
$response->assertStatus(201);
}
}
Cookie
你可以使用 withCookie 或 withCookies 方法,在发起请求前设置 Cookie 值。withCookie 方法接收 Cookie 名称和值两个参数,而 withCookies 方法接收一个名称 / 值键值对数组:
<?php
test('interacting with cookies', function () {
$response = $this->withCookie('color', 'blue')->get('/');
$response = $this->withCookies([
'color' => 'blue',
'name' => 'Taylor',
])->get('/');
//
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_interacting_with_cookies(): void
{
$response = $this->withCookie('color', 'blue')->get('/');
$response = $this->withCookies([
'color' => 'blue',
'name' => 'Taylor',
])->get('/');
//
}
}
会话 / 身份验证
Laravel 提供了多个辅助方法,用于在 HTTP 测试期间与会话交互。首先,你可以使用 withSession 方法,将给定数组设为会话数据。这有助于在向应用发起请求之前,预先向会话中写入数据:
<?php
test('interacting with the session', function () {
$response = $this->withSession(['banned' => false])->get('/');
//
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_interacting_with_the_session(): void
{
$response = $this->withSession(['banned' => false])->get('/');
//
}
}
Laravel 的会话通常用于维护当前已认证用户的状态。因此,actingAs 辅助方法让你能够轻松地将给定用户认证为当前用户。例如,我们可以使用模型工厂生成并认证一个用户:
<?php
use App\Models\User;
test('an action that requires authentication', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)
->withSession(['banned' => false])
->get('/');
//
});
<?php
namespace Tests\Feature;
use App\Models\User;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_an_action_that_requires_authentication(): void
{
$user = User::factory()->create();
$response = $this->actingAs($user)
->withSession(['banned' => false])
->get('/');
//
}
}
你也可以将 guard 名称作为第二个参数传递给 actingAs 方法,指定用于认证该用户的 guard。传递给 actingAs 方法的 guard 也会在本次测试期间成为默认 guard:
$this->actingAs($user, 'web');
如果你想确保请求处于未认证状态,可以使用 actingAsGuest 方法:
$this->actingAsGuest();
调试响应
向应用发起测试请求后,你可以使用 dump、dumpHeaders 和 dumpSession 方法,查看并调试响应内容:
<?php
test('basic test', function () {
$response = $this->get('/');
$response->dump();
$response->dumpHeaders();
$response->dumpSession();
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的测试示例。
*/
public function test_basic_test(): void
{
$response = $this->get('/');
$response->dump();
$response->dumpHeaders();
$response->dumpSession();
}
}
你也可以使用 dd、ddHeaders、ddBody、ddJson 和 ddSession 方法,输出响应信息并停止执行:
<?php
test('basic test', function () {
$response = $this->get('/');
$response->dd();
$response->ddHeaders();
$response->ddBody();
$response->ddJson();
$response->ddSession();
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的测试示例。
*/
public function test_basic_test(): void
{
$response = $this->get('/');
$response->dd();
$response->ddHeaders();
$response->ddBody();
$response->ddJson();
$response->ddSession();
}
}
异常处理
有时,你可能需要测试应用是否抛出了特定异常。为此,可以通过 Exceptions facade 模拟异常处理器。模拟异常处理器后,你可以使用 assertReported 和 assertNotReported 方法,对请求期间抛出的异常进行断言:
<?php
use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;
test('exception is thrown', function () {
Exceptions::fake();
$response = $this->get('/order/1');
// 断言抛出了异常……
Exceptions::assertReported(InvalidOrderException::class);
// 对异常进行断言……
Exceptions::assertReported(function (InvalidOrderException $e) {
return $e->getMessage() === 'The order was invalid.';
});
});
<?php
namespace Tests\Feature;
use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的测试示例。
*/
public function test_exception_is_thrown(): void
{
Exceptions::fake();
$response = $this->get('/');
// 断言抛出了异常……
Exceptions::assertReported(InvalidOrderException::class);
// 对异常进行断言……
Exceptions::assertReported(function (InvalidOrderException $e) {
return $e->getMessage() === 'The order was invalid.';
});
}
}
assertNotReported 和 assertNothingReported 方法可用于断言请求期间未抛出给定异常,或没有抛出任何异常:
Exceptions::assertNotReported(InvalidOrderException::class);
Exceptions::assertNothingReported();
你可以在发起请求之前调用 withoutExceptionHandling 方法,完全禁用该请求的异常处理:
$response = $this->withoutExceptionHandling()->get('/');
此外,如果你想确保应用没有使用 PHP 语言或应用所用库中已弃用的功能,可以在发起请求前调用 withoutDeprecationHandling 方法。禁用弃用警告处理后,弃用警告会转换为异常,从而导致测试失败:
$response = $this->withoutDeprecationHandling()->get('/');
assertThrows 方法可用于断言给定闭包中的代码抛出了指定类型的异常:
$this->assertThrows(
fn () => (new ProcessOrder)->execute(),
OrderInvalid::class
);
如果你想检查抛出的异常并对其进行断言,可以将闭包作为第二个参数传递给 assertThrows 方法:
$this->assertThrows(
fn () => (new ProcessOrder)->execute(),
fn (OrderInvalid $e) => $e->orderId() === 123
);
assertDoesntThrow 方法可用于断言给定闭包中的代码没有抛出任何异常:
$this->assertDoesntThrow(fn () => (new ProcessOrder)->execute());
测试 JSON API
Laravel 还提供了多个辅助方法,用于测试 JSON API 及其响应。例如,json、getJson、postJson、putJson、patchJson、deleteJson 和 optionsJson 方法可用于通过不同的 HTTP 动词发起 JSON 请求。你也可以轻松地向这些方法传递数据和请求头。首先,让我们编写一个测试,向 /api/user 发起 POST 请求,并断言返回了预期的 JSON 数据:
<?php
test('making an api request', function () {
$response = $this->postJson('/api/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertJson([
'created' => true,
]);
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*/
public function test_making_an_api_request(): void
{
$response = $this->postJson('/api/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertJson([
'created' => true,
]);
}
}
此外,你可以像访问数组变量一样访问响应中的 JSON 数据,方便检查 JSON 响应返回的各个值:
expect($response['created'])->toBeTrue();
$this->assertTrue($response['created']);
[!NOTE]
assertJson方法会将响应转换为数组,以验证给定数组是否包含在应用返回的 JSON 响应中。因此,即使 JSON 响应中还有其他属性,只要给定片段存在,测试仍会通过。
断言 JSON 完全匹配
如前所述,assertJson 方法可用于断言 JSON 响应中包含某个 JSON 片段。如果你想验证给定数组与应用返回的 JSON 完全匹配,应该使用 assertExactJson 方法:
<?php
test('asserting an exact json match', function () {
$response = $this->postJson('/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertExactJson([
'created' => true,
]);
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*/
public function test_asserting_an_exact_json_match(): void
{
$response = $this->postJson('/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertExactJson([
'created' => true,
]);
}
}
断言 JSON 路径
如果你想验证 JSON 响应在指定路径处包含给定数据,应该使用 assertJsonPath 方法:
<?php
test('asserting a json path value', function () {
$response = $this->postJson('/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertJsonPath('team.owner.name', 'Darian');
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
/**
* 一个基本的功能测试示例。
*/
public function test_asserting_a_json_paths_value(): void
{
$response = $this->postJson('/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertJsonPath('team.owner.name', 'Darian');
}
}
assertJsonPath 方法也接收闭包,用于动态判断断言是否通过:
$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);
如果你需要一次断言多个 JSON 路径,可以使用 assertJsonPaths 方法。每个路径的预期值也可以是闭包:
$response->assertJsonPaths([
'team.owner.name' => 'Darian',
'team.owner.email' => fn (string $email) => str($email)->is('*@laravel.com'),
'team.members.0.name' => 'Sally',
]);
你可以使用 assertJsonMissingPaths 方法,断言响应中不存在多个 JSON 路径:
$response->assertJsonMissingPaths([
'team.owner.password',
'team.members.0.api_token',
]);
链式 JSON 测试
Laravel 还提供了优雅的方式,让你以链式调用测试应用的 JSON 响应。首先,向 assertJson 方法传递一个闭包。调用该闭包时,会传入一个 Illuminate\Testing\Fluent\AssertableJson 实例,用于对应用返回的 JSON 进行断言。where 方法可用于断言 JSON 中的特定属性,而 missing 方法可用于断言 JSON 中不存在某个属性:
use Illuminate\Testing\Fluent\AssertableJson;
test('fluent json', function () {
$response = $this->getJson('/users/1');
$response
->assertJson(fn (AssertableJson $json) =>
$json->where('id', 1)
->where('name', 'Victoria Faith')
->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
->whereNot('status', 'pending')
->missing('password')
->etc()
);
});
use Illuminate\Testing\Fluent\AssertableJson;
/**
* 一个基本的功能测试示例。
*/
public function test_fluent_json(): void
{
$response = $this->getJson('/users/1');
$response
->assertJson(fn (AssertableJson $json) =>
$json->where('id', 1)
->where('name', 'Victoria Faith')
->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
->whereNot('status', 'pending')
->missing('password')
->etc()
);
}
理解 etc 方法
在上面的示例中,你可能注意到我们在断言链末尾调用了 etc 方法。该方法告知 Laravel,JSON 对象中可能还存在其他属性。如果没有使用 etc 方法,而 JSON 对象中存在未经断言的其他属性,测试就会失败。
这种行为旨在防止你无意间在 JSON 响应中暴露敏感信息:你必须显式地对属性进行断言,或者通过 etc 方法明确允许额外的属性。
不过,你需要注意,未在断言链中调用 etc 方法,并不能保证 JSON 对象内部嵌套的数组中不会出现额外属性。etc 方法只针对调用它的嵌套层级控制是否允许额外属性。
断言属性存在 / 缺失
要断言某个属性存在或缺失,可以使用 has 和 missing 方法:
$response->assertJson(fn (AssertableJson $json) =>
$json->has('data')
->missing('message')
);
此外,hasAll 和 missingAll 方法允许你同时断言多个属性存在或缺失:
$response->assertJson(fn (AssertableJson $json) =>
$json->hasAll(['status', 'data'])
->missingAll(['message', 'code'])
);
你可以使用 hasAny 方法,判断给定的一组属性中是否至少有一个存在:
$response->assertJson(fn (AssertableJson $json) =>
$json->has('status')
->hasAny('data', 'message', 'code')
);
对 JSON 集合进行断言
路由通常会返回包含多个项目的 JSON 响应,例如多个用户:
Route::get('/users', function () {
return User::all();
});
在这种情况下,我们可以使用链式 JSON 对象的 has 方法,对响应中包含的用户进行断言。例如,先断言 JSON 响应包含三个用户,再使用 first 方法对集合中的第一个用户进行断言。first 方法接收一个闭包,该闭包会接收另一个可用于断言的 JSON 对象,供我们对 JSON 集合中的第一个对象进行断言:
$response
->assertJson(fn (AssertableJson $json) =>
$json->has(3)
->first(fn (AssertableJson $json) =>
$json->where('id', 1)
->where('name', 'Victoria Faith')
->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
->missing('password')
->etc()
)
);
如果你想对 JSON 集合中的每个项目进行相同的断言,可以使用 each 方法:
$response
->assertJson(fn (AssertableJson $json) =>
$json->has(3)
->each(fn (AssertableJson $json) =>
$json->whereType('id', 'integer')
->whereType('name', 'string')
->whereType('email', 'string')
->missing('password')
->etc()
)
);
限定 JSON 集合断言的作用范围
有时,应用的路由会返回分配给命名键的 JSON 集合:
Route::get('/users', function () {
return [
'meta' => [...],
'users' => User::all(),
];
});
测试这些路由时,你可以使用 has 方法断言集合中的项目数量。此外,还可以使用 has 方法限定一系列断言的作用范围:
$response
->assertJson(fn (AssertableJson $json) =>
$json->has('meta')
->has('users', 3)
->has('users.0', fn (AssertableJson $json) =>
$json->where('id', 1)
->where('name', 'Victoria Faith')
->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
->missing('password')
->etc()
)
);
你也可以只调用一次 has 方法,并将闭包作为第三个参数传入,而无需分别调用两次 has 方法对 users 集合进行断言。此时,闭包会被自动调用,其作用范围会限定为集合中的第一个项目:
$response
->assertJson(fn (AssertableJson $json) =>
$json->has('meta')
->has('users', 3, fn (AssertableJson $json) =>
$json->where('id', 1)
->where('name', 'Victoria Faith')
->where('email', fn (string $email) => str($email)->is('victoria@gmail.com'))
->missing('password')
->etc()
)
);
断言 JSON 类型
你可能只想断言 JSON 响应中属性的类型。Illuminate\Testing\Fluent\AssertableJson 类提供了 whereType 和 whereAllType 方法,用于实现这一点:
$response->assertJson(fn (AssertableJson $json) =>
$json->whereType('id', 'integer')
->whereAllType([
'users.0.name' => 'string',
'meta' => 'array'
])
);
你可以使用 | 字符指定多个类型,或将类型数组作为第二个参数传递给 whereType 方法。只要响应值属于所列类型之一,断言就会通过:
$response->assertJson(fn (AssertableJson $json) =>
$json->whereType('name', 'string|null')
->whereType('id', ['string', 'integer'])
);
whereType 和 whereAllType 方法支持以下类型:string、integer、double、boolean、array 和 null。
测试文件上传
Illuminate\Http\UploadedFile 类提供了 fake 方法,可用于生成测试用的模拟文件或图片。结合 Storage facade 的 fake 方法,可以大幅简化文件上传测试。例如,你可以结合这两个功能,轻松测试头像上传表单:
<?php
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
test('avatars can be uploaded', function () {
Storage::fake('avatars');
$file = UploadedFile::fake()->image('avatar.jpg');
$response = $this->post('/avatar', [
'avatar' => $file,
]);
Storage::disk('avatars')->assertExists($file->hashName());
});
<?php
namespace Tests\Feature;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_avatars_can_be_uploaded(): void
{
Storage::fake('avatars');
$file = UploadedFile::fake()->image('avatar.jpg');
$response = $this->post('/avatar', [
'avatar' => $file,
]);
Storage::disk('avatars')->assertExists($file->hashName());
}
}
如果你想断言给定文件不存在,可以使用 Storage facade 提供的 assertMissing 方法:
Storage::fake('avatars');
// ...
Storage::disk('avatars')->assertMissing('missing.jpg');
自定义模拟文件
使用 UploadedFile 类提供的 fake 方法创建文件时,你可以指定图片的宽度、高度和大小(单位为 KB),以便更好地测试应用的验证规则:
UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);
除了创建图片,你还可以使用 create 方法创建任意其他类型的文件:
UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);
如有需要,你可以向该方法传递 $mimeType 参数,显式指定文件应返回的 MIME 类型:
UploadedFile::fake()->create(
'document.pdf', $sizeInKilobytes, 'application/pdf'
);
测试视图
Laravel 还允许你在不向应用发起模拟 HTTP 请求的情况下渲染视图。为此,可以在测试中调用 view 方法。该方法接收视图名称和一个可选的数据数组,并返回 Illuminate\Testing\TestView 实例。该实例提供了多个方法,方便你对视图内容进行断言:
<?php
test('a welcome view can be rendered', function () {
$view = $this->view('welcome', ['name' => 'Taylor']);
$view->assertSee('Taylor');
});
<?php
namespace Tests\Feature;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_a_welcome_view_can_be_rendered(): void
{
$view = $this->view('welcome', ['name' => 'Taylor']);
$view->assertSee('Taylor');
}
}
TestView 类提供了以下断言方法:assertSee、assertSeeInOrder、assertSeeText、assertSeeTextInOrder、assertDontSee 和 assertDontSeeText。
如有需要,你可以将 TestView 实例转换为字符串,获取渲染后的原始视图内容:
$contents = (string) $this->view('welcome');
共享错误
某些视图可能依赖 Laravel 提供的全局错误包中的错误。你可以使用 withViewErrors 方法向错误包填充错误消息:
$view = $this->withViewErrors([
'name' => ['Please provide a valid name.']
])->view('form');
$view->assertSee('Please provide a valid name.');
渲染 Blade 和组件
如有需要,你可以使用 blade 方法解析并渲染原始的 Blade 字符串。与 view 方法一样,blade 方法会返回一个 Illuminate\Testing\TestView 实例:
$view = $this->blade(
'<x-component :name="$name" />',
['name' => 'Taylor']
);
$view->assertSee('Taylor');
你可以使用 component 方法解析并渲染一个 Blade 组件。该方法会返回一个 Illuminate\Testing\TestComponent 实例:
$view = $this->component(Profile::class, ['name' => 'Taylor']);
$view->assertSee('Taylor');
缓存路由
每次测试运行前,Laravel 都会启动一个全新的应用实例,包括收集所有已定义的路由。如果应用有很多路由文件,你可能希望为测试用例添加 Illuminate\Foundation\Testing\WithCachedRoutes trait。使用该 trait 时,路由只会构建一次并存储在内存中,这意味着整个测试套件只需执行一次路由收集过程:
<?php
use App\Http\Controllers\UserController;
use Illuminate\Foundation\Testing\WithCachedRoutes;
pest()->use(WithCachedRoutes::class);
test('basic example', function () {
$this->get(action([UserController::class, 'index']));
// ...
});
<?php
namespace Tests\Feature;
use App\Http\Controllers\UserController;
use Illuminate\Foundation\Testing\WithCachedRoutes;
use Tests\TestCase;
class BasicTest extends TestCase
{
use WithCachedRoutes;
/**
* 一个基本的功能测试示例。
*/
public function test_basic_example(): void
{
$response = $this->get(action([UserController::class, 'index']));
// ...
}
}
可用断言
响应断言
Laravel 的 Illuminate\Testing\TestResponse 类提供了多种自定义断言方法,供你测试应用。你可以在 json、get、post、put 和 delete 测试方法返回的响应上调用这些断言:
assertAccepted
assertBadRequest
assertClientError
assertConflict
assertCookie
assertCookieExpired
assertCookieNotExpired
assertCookieMissing
assertCreated
assertDontSee
assertDontSeeText
assertDownload
assertExactJson
assertExactJsonStructure
assertFailedDependency
assertForbidden
assertFound
assertGone
assertHeader
assertHeaderContains
assertHeaderMissing
assertInternalServerError
assertJson
assertJsonCount
assertJsonFragment
assertJsonIsArray
assertJsonIsObject
assertJsonMissing
assertJsonMissingExact
assertJsonMissingValidationErrors
assertJsonPath
assertJsonPaths
assertJsonMissingPath
assertJsonMissingPaths
assertJsonStructure
assertJsonValidationErrors
assertJsonValidationErrorFor
assertLocation
assertMethodNotAllowed
assertMovedPermanently
assertContent
assertNoContent
assertStreamed
assertStreamedContent
assertNotFound
assertOk
assertPaymentRequired
assertPlainCookie
assertRedirect
assertRedirectBack
assertRedirectBackWithErrors
assertRedirectBackWithoutErrors
assertRedirectContains
assertRedirectToRoute
assertRedirectToSignedRoute
assertRequestTimeout
assertSee
assertSeeInOrder
assertSeeText
assertSeeTextInOrder
assertServerError
assertServiceUnavailable
assertSessionHas
assertSessionHasInput
assertSessionHasAll
assertSessionHasErrors
assertSessionHasErrorsIn
assertSessionHasNoErrors
assertSessionDoesntHaveErrors
assertSessionMissing
assertSessionMissingInput
assertStatus
assertSuccessful
assertTooManyRequests
assertUnauthorized
assertUnprocessable
assertUnsupportedMediaType
assertValid
assertInvalid
assertViewHas
assertViewHasAll
assertViewIs
assertViewMissing
assertAccepted
断言响应的 HTTP 状态码为已接受(202):
$response->assertAccepted();
assertBadRequest
断言响应的 HTTP 状态码为错误请求(400):
$response->assertBadRequest();
assertClientError
断言响应的 HTTP 状态码为客户端错误(>= 400,< 500):
$response->assertClientError();
assertConflict
断言响应的 HTTP 状态码为冲突(409):
$response->assertConflict();
assertCookie
断言响应包含给定的 Cookie:
$response->assertCookie($cookieName, $value = null);
assertCookieExpired
断言响应包含给定的 Cookie,且该 Cookie 已过期:
$response->assertCookieExpired($cookieName);
assertCookieNotExpired
断言响应包含给定的 Cookie,且该 Cookie 尚未过期:
$response->assertCookieNotExpired($cookieName);
assertCookieMissing
断言响应不包含给定的 Cookie:
$response->assertCookieMissing($cookieName);
assertCreated
断言响应的 HTTP 状态码为 201:
$response->assertCreated();
assertDontSee
断言应用返回的响应中不包含给定字符串。除非将第二个参数设为 false,否则此断言会自动转义给定字符串:
$response->assertDontSee($value, $escape = true);
assertDontSeeText
断言响应文本中不包含给定字符串。除非将第二个参数设为 false,否则此断言会自动转义给定字符串。该方法会在执行断言前,将响应内容传递给 PHP 的 strip_tags 函数:
$response->assertDontSeeText($value, $escape = true);
assertDownload
断言响应为「下载」响应。通常,这意味着返回该响应的路由返回了 Response::download 响应、BinaryFileResponse 或 Storage::download 响应:
$response->assertDownload();
如有需要,你还可以断言下载文件被指定了给定的文件名:
$response->assertDownload('image.jpg');
assertExactJson
断言响应与给定的 JSON 数据完全匹配:
$response->assertExactJson(array $data);
assertExactJsonStructure
断言响应与给定的 JSON 结构完全匹配:
$response->assertExactJsonStructure(array $data);
该方法是 assertJsonStructure 的更严格版本。与 assertJsonStructure 不同,如果响应包含预期 JSON 结构中未明确列出的任何键,该断言就会失败。
assertFailedDependency
断言响应的 HTTP 状态码为依赖失败(424):
$response->assertFailedDependency();
assertForbidden
断言响应的 HTTP 状态码为禁止访问(403):
$response->assertForbidden();
assertFound
断言响应的 HTTP 状态码为已找到(302):
$response->assertFound();
assertGone
断言响应的 HTTP 状态码为已移除(410):
$response->assertGone();
assertHeader
断言响应中存在给定的响应头名称和值:
$response->assertHeader($headerName, $value = null);
assertHeaderContains
断言给定的响应头包含指定的子字符串值:
$response->assertHeaderContains($headerName, $value);
assertHeaderMissing
断言响应中不存在给定的响应头:
$response->assertHeaderMissing($headerName);
assertInternalServerError
断言响应的 HTTP 状态码为「内部服务器错误」(500):
$response->assertInternalServerError();
assertJson
断言响应包含给定的 JSON 数据:
$response->assertJson(array $data, $strict = false);
assertJson 方法会将响应转换为数组,以验证给定数组是否包含在应用返回的 JSON 响应中。因此,即使 JSON 响应中还有其他属性,只要给定片段存在,测试仍会通过。
assertJsonCount
断言响应 JSON 中给定键对应的数组包含预期数量的项目:
$response->assertJsonCount($count, $key = null);
assertJsonFragment
断言响应中的任意位置包含给定的 JSON 数据:
Route::get('/users', function () {
return [
'users' => [
[
'name' => 'Taylor Otwell',
],
],
];
});
$response->assertJsonFragment(['name' => 'Taylor Otwell']);
assertJsonIsArray
断言响应 JSON 为数组:
$response->assertJsonIsArray();
assertJsonIsObject
断言响应 JSON 为对象:
$response->assertJsonIsObject();
assertJsonMissing
断言响应不包含给定的 JSON 数据:
$response->assertJsonMissing(array $data);
assertJsonMissingExact
断言响应中不存在与给定 JSON 数据完全匹配的内容:
$response->assertJsonMissingExact(array $data);
assertJsonMissingValidationErrors
断言响应中给定的键没有 JSON 验证错误:
$response->assertJsonMissingValidationErrors($keys);
[!NOTE]
你可以使用更通用的 assertValid 方法,断言响应没有以 JSON 返回的验证错误,并且没有错误被暂存到会话中。
assertJsonPath
断言响应在指定路径处包含给定数据:
$response->assertJsonPath($path, $expectedValue);
例如,应用返回了以下 JSON 响应:
{
"user": {
"name": "Steve Schoger"
}
}
你可以像下面这样,断言 user 对象的 name 属性与给定值匹配:
$response->assertJsonPath('user.name', 'Steve Schoger');
assertJsonPaths
断言响应在多个指定路径处包含给定数据:
$response->assertJsonPaths(array $paths);
例如,你可以一次断言响应中的多个值:
$response->assertJsonPaths([
'user.name' => 'Steve Schoger',
'user.email' => fn (string $email) => str($email)->endsWith('@laravel.com'),
]);
assertJsonMissingPath
断言响应中不存在给定路径:
$response->assertJsonMissingPath($path);
例如,应用返回了以下 JSON 响应:
{
"user": {
"name": "Steve Schoger"
}
}
你可以断言响应中不存在 user 对象的 email 属性:
$response->assertJsonMissingPath('user.email');
assertJsonMissingPaths
断言响应中不存在给定的多个路径:
$response->assertJsonMissingPaths($paths);
例如,你可以断言响应中不存在多个路径:
$response->assertJsonMissingPaths([
'user.email',
'user.password',
]);
assertJsonStructure
断言响应具有给定的 JSON 结构:
$response->assertJsonStructure(array $structure);
例如,应用返回的 JSON 响应包含以下数据:
{
"user": {
"name": "Steve Schoger"
}
}
你可以像下面这样,断言 JSON 结构符合预期:
$response->assertJsonStructure([
'user' => [
'name',
]
]);
有时,应用返回的 JSON 响应中可能包含对象数组:
{
"user": [
{
"name": "Steve Schoger",
"age": 55,
"location": "Earth"
},
{
"name": "Mary Schoger",
"age": 60,
"location": "Earth"
}
]
}
在这种情况下,你可以使用 * 字符,断言数组中所有对象的结构:
$response->assertJsonStructure([
'user' => [
'*' => [
'name',
'age',
'location'
]
]
]);
assertJsonValidationErrors
断言响应中给定的键存在指定的 JSON 验证错误。验证错误以 JSON 结构返回,而非暂存到会话中时,应该使用该方法:
$response->assertJsonValidationErrors(array $data, $responseKey = 'errors');
[!NOTE]
你可以使用更通用的 assertInvalid 方法,断言响应包含以 JSON 返回的验证错误,或者错误已被暂存到会话中。
assertJsonValidationErrorFor
断言响应中给定的键存在任意 JSON 验证错误:
$response->assertJsonValidationErrorFor(string $key, $responseKey = 'errors');
assertMethodNotAllowed
断言响应的 HTTP 状态码为方法不允许(405):
$response->assertMethodNotAllowed();
assertMovedPermanently
断言响应的 HTTP 状态码为永久移动(301):
$response->assertMovedPermanently();
assertLocation
断言响应的 Location 头包含给定的 URI 值:
$response->assertLocation($uri);
assertContent
断言给定字符串与响应内容匹配:
$response->assertContent($value);
assertNoContent
断言响应具有给定的 HTTP 状态码,且没有内容:
$response->assertNoContent($status = 204);
assertStreamed
断言响应为流式响应:
$response->assertStreamed();
assertStreamedContent
断言给定字符串与流式响应的内容匹配:
$response->assertStreamedContent($value);
assertNotFound
断言响应的 HTTP 状态码为未找到(404):
$response->assertNotFound();
assertOk
断言响应的 HTTP 状态码为 200:
$response->assertOk();
assertPaymentRequired
断言响应的 HTTP 状态码为需要付款(402):
$response->assertPaymentRequired();
assertPlainCookie
断言响应包含给定的未加密 Cookie:
$response->assertPlainCookie($cookieName, $value = null);
assertRedirect
断言响应重定向到给定的 URI:
$response->assertRedirect($uri = null);
assertRedirectBack
断言响应重定向回上一页:
$response->assertRedirectBack();
assertRedirectBackWithErrors
断言响应重定向回上一页,且会话中包含给定的错误:
$response->assertRedirectBackWithErrors(
array $keys = [], $format = null, $errorBag = 'default'
);
assertRedirectBackWithoutErrors
断言响应重定向回上一页,且会话中没有任何错误消息:
$response->assertRedirectBackWithoutErrors();
assertRedirectContains
断言响应重定向到的 URI 包含给定字符串:
$response->assertRedirectContains($string);
assertRedirectToRoute
断言响应重定向到给定的命名路由:
$response->assertRedirectToRoute($name, $parameters = []);
assertRedirectToSignedRoute
断言响应重定向到给定的签名路由:
$response->assertRedirectToSignedRoute($name = null, $parameters = []);
assertRequestTimeout
断言响应的 HTTP 状态码为请求超时(408):
$response->assertRequestTimeout();
assertSee
断言响应中包含给定字符串。除非将第二个参数设为 false,否则此断言会自动转义给定字符串:
$response->assertSee($value, $escape = true);
assertSeeInOrder
断言响应中按顺序包含给定的多个字符串。除非将第二个参数设为 false,否则此断言会自动转义给定字符串:
$response->assertSeeInOrder(array $values, $escape = true);
assertSeeText
断言响应文本中包含给定字符串。除非将第二个参数设为 false,否则此断言会自动转义给定字符串。在执行断言前,响应内容会传递给 PHP 的 strip_tags 函数:
$response->assertSeeText($value, $escape = true);
assertSeeTextInOrder
断言响应文本中按顺序包含给定的多个字符串。除非将第二个参数设为 false,否则此断言会自动转义给定字符串。在执行断言前,响应内容会传递给 PHP 的 strip_tags 函数:
$response->assertSeeTextInOrder(array $values, $escape = true);
assertServerError
断言响应的 HTTP 状态码为服务器错误(>= 500,< 600):
$response->assertServerError();
assertServiceUnavailable
断言响应的 HTTP 状态码为「服务不可用」(503):
$response->assertServiceUnavailable();
assertSessionHas
断言会话中包含给定的数据:
$response->assertSessionHas($key, $value = null);
如有需要,你可以将闭包作为第二个参数传递给 assertSessionHas 方法。如果闭包返回 true,断言就会通过:
$response->assertSessionHas($key, function (User $value) {
return $value->name === 'Taylor Otwell';
});
assertSessionHasInput
断言会话的暂存输入数组中包含给定值:
$response->assertSessionHasInput($key, $value = null);
如有需要,你可以将闭包作为第二个参数传递给 assertSessionHasInput 方法。如果闭包返回 true,断言就会通过:
use Illuminate\Support\Facades\Crypt;
$response->assertSessionHasInput($key, function (string $value) {
return Crypt::decryptString($value) === 'secret';
});
assertSessionHasAll
断言会话中包含给定数组中的所有键 / 值对:
$response->assertSessionHasAll(array $data);
例如,如果应用的会话中包含 name 和 status 键,你可以像下面这样,断言这两个键都存在且具有指定的值:
$response->assertSessionHasAll([
'name' => 'Taylor Otwell',
'status' => 'active',
]);
assertSessionHasErrors
断言会话中给定的 $keys 存在错误。如果 $keys 是关联数组,则断言会话中每个字段(键)都有指定的错误消息(值)。测试将验证错误暂存到会话、而非以 JSON 结构返回错误的路由时,应该使用该方法:
$response->assertSessionHasErrors(
array $keys = [], $format = null, $errorBag = 'default'
);
例如,要断言 name 和 email 字段的验证错误消息已被暂存到会话中,你可以像下面这样调用 assertSessionHasErrors 方法:
$response->assertSessionHasErrors(['name', 'email']);
或者,你可以断言给定字段具有特定的验证错误消息:
$response->assertSessionHasErrors([
'name' => 'The given name was invalid.'
]);
[!NOTE]
你可以使用更通用的 assertInvalid 方法,断言响应包含以 JSON 返回的验证错误,或者错误已被暂存到会话中。
assertSessionHasErrorsIn
断言会话中指定的错误包包含给定 $keys 的错误。如果 $keys 是关联数组,则断言该错误包中每个字段(键)都有指定的错误消息(值):
$response->assertSessionHasErrorsIn($errorBag, $keys = [], $format = null);
assertSessionHasNoErrors
断言会话中没有验证错误:
$response->assertSessionHasNoErrors();
assertSessionDoesntHaveErrors
断言会话中给定的键没有验证错误:
$response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default');
[!NOTE]
你可以使用更通用的 assertValid 方法,断言响应没有以 JSON 返回的验证错误,并且没有错误被暂存到会话中。
assertSessionMissing
断言会话中不存在给定的键:
$response->assertSessionMissing($key);
assertSessionMissingInput
断言会话的暂存输入数组中不存在给定的输入键:
$response->assertSessionMissingInput($key);
assertStatus
断言响应具有给定的 HTTP 状态码:
$response->assertStatus($code);
assertSuccessful
断言响应的 HTTP 状态码为成功状态(>= 200 且 < 300):
$response->assertSuccessful();
assertTooManyRequests
断言响应的 HTTP 状态码为请求过多(429):
$response->assertTooManyRequests();
assertUnauthorized
断言响应的 HTTP 状态码为未授权(401):
$response->assertUnauthorized();
assertUnprocessable
断言响应的 HTTP 状态码为无法处理的实体(422):
$response->assertUnprocessable();
assertUnsupportedMediaType
断言响应的 HTTP 状态码为不支持的媒体类型(415):
$response->assertUnsupportedMediaType();
assertValid
断言响应中给定的键没有验证错误。验证错误以 JSON 结构返回或被暂存到会话中时,都可以使用该方法进行断言:
// 断言不存在验证错误……
$response->assertValid();
// 断言给定的键没有验证错误……
$response->assertValid(['name', 'email']);
assertInvalid
断言响应中给定的键存在验证错误。验证错误以 JSON 结构返回或被暂存到会话中时,都可以使用该方法进行断言:
$response->assertInvalid(['name', 'email']);
你也可以断言给定的键具有特定的验证错误消息。此时,你可以提供完整的错误消息,也可以只提供其中一小部分:
$response->assertInvalid([
'name' => 'The name field is required.',
'email' => 'valid email address',
]);
如果你想断言只有给定的字段存在验证错误,可以使用 assertOnlyInvalid 方法:
$response->assertOnlyInvalid(['name', 'email']);
assertViewHas
断言响应中的视图包含给定的数据:
$response->assertViewHas($key, $value = null);
将闭包作为第二个参数传递给 assertViewHas 方法,可以让你检查特定的视图数据,并对其进行断言:
$response->assertViewHas('user', function (User $user) {
return $user->name === 'Taylor';
});
此外,你可以像访问数组变量一样访问响应中的视图数据,方便对其进行检查:
expect($response['name'])->toBe('Taylor');
$this->assertEquals('Taylor', $response['name']);
assertViewHasAll
断言响应中的视图包含给定的数据列表:
$response->assertViewHasAll(array $data);
你可以使用该方法,仅断言视图包含与给定键对应的数据:
$response->assertViewHasAll([
'name',
'email',
]);
或者,你可以断言视图数据存在且具有指定的值:
$response->assertViewHasAll([
'name' => 'Taylor Otwell',
'email' => 'taylor@example.com,',
]);
assertViewIs
断言路由返回了给定的视图:
$response->assertViewIs($value);
assertViewMissing
断言给定的数据键没有提供给应用响应所返回的视图:
$response->assertViewMissing($key);
身份验证断言
Laravel 还提供了多种与身份验证相关的断言,供你在应用的功能测试中使用。请注意,这些方法是在测试类本身上调用的,而非在 get、post 等方法返回的 Illuminate\Testing\TestResponse 实例上调用。
assertAuthenticated
断言用户已通过身份验证:
$this->assertAuthenticated($guard = null);
assertGuest
断言用户未通过身份验证:
$this->assertGuest($guard = null);
assertAuthenticatedAs
断言指定用户已通过身份验证:
$this->assertAuthenticatedAs($user, $guard = null);
验证断言
Laravel 提供了两个主要的验证相关断言,用于确保请求中提供的数据有效或无效。
assertValid
断言响应中给定的键没有验证错误。验证错误以 JSON 结构返回或被暂存到会话中时,都可以使用该方法进行断言:
// 断言不存在验证错误……
$response->assertValid();
// 断言给定的键没有验证错误……
$response->assertValid(['name', 'email']);
assertInvalid
断言响应中给定的键存在验证错误。验证错误以 JSON 结构返回或被暂存到会话中时,都可以使用该方法进行断言:
$response->assertInvalid(['name', 'email']);
你也可以断言给定的键具有特定的验证错误消息。此时,你可以提供完整的错误消息,也可以只提供其中一小部分:
$response->assertInvalid([
'name' => 'The name field is required.',
'email' => 'valid email address',
]);
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
Laravel 13 中文文档
关于 LearnKu
推荐文章: