简介

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\Http\Response 实例,而是返回 Illuminate\Testing\TestResponse 实例,该实例提供了多种实用的断言,可用于检查应用的响应:

<?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);
    }
}

通常,每个测试应只向你的应用发起一次请求。若在单个测试方法中执行多个请求,可能会出现意外行为。

为方便起见,在运行测试时,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 门面来“伪造”异常处理器。异常处理器被伪造后,你可以使用 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']);

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('[email protected]'))
                ->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('[email protected]'))
                ->whereNot('status', 'pending')
                ->missing('password')
                ->etc()
        );
}

理解 etc 方法

在上面的示例中,你可能注意到了我们在断言链的末尾调用了 etc 方法。该方法告知 Laravel,JSON 对象上可能存在其他属性。如果未使用 etc 方法,一旦 JSON 对象中存在未进行断言的其他属性,测试将会失败。

这种行为的初衷是防止你在 JSON 响应中无意中暴露敏感信息,它会强制你要么显式地对属性进行断言,要么通过 etc 方法显式允许额外属性的存在。

不过,你应当注意,在断言链中不包含 etc 方法,并不能保证 JSON 对象中嵌套的数组不会添加额外属性。etc 方法仅确保在调用 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('[email protected]'))
                    ->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('[email protected]'))
                    ->missing('password')
                    ->etc()
            )
    );

不过,你无需分别调用两次 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('[email protected]'))
                    ->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 门面的 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 门面提供的 assertMissing 方法:

Storage::fake('avatars');

// ...

Storage::disk('avatars')->assertMissing('missing.jpg');

虚拟文件自定义

使用 UploadedFile 类提供的 fake 方法创建文件时,你可以指定图像的宽度、高度和大小(单位为千字节),以便更有效地测试应用程序的验证规则:

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 方法。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 组件。component 方法返回 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;

    /**
     * A basic functional test example.
     */
    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

断言响应具有 accepted(202)HTTP 状态码:

$response->assertAccepted();

assertBadRequest

断言响应具有 bad request(400)HTTP 状态码:

$response->assertBadRequest();

assertClientError

断言响应具有客户端错误(>= 400,< 500)HTTP 状态码:

$response->assertClientError();

assertConflict

断言响应具有 conflict(409)HTTP 状态码:

$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

断言响应具有 201 HTTP 状态码:

$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

断言响应具有 failed dependency(424)HTTP 状态码:

$response->assertFailedDependency();

assertForbidden

断言响应具有 forbidden(403)HTTP 状态码:

$response->assertForbidden();

assertFound

断言响应具有 found(302)HTTP 状态码:

$response->assertFound();

assertGone

断言响应具有 gone(410)HTTP 状态码:

$response->assertGone();

assertHeader

断言响应中存在指定的头部及其值:

$response->assertHeader($headerName, $value = null);

assertHeaderContains

断言指定的头部包含给定的子字符串值:

$response->assertHeaderContains($headerName, $value);

assertHeaderMissing

断言响应中不存在指定的头部:

$response->assertHeaderMissing($headerName);

assertInternalServerError

断言响应具有“Internal Server Error”(500)HTTP 状态码:

$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);

更通用的 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');

更通用的 assertInvalid 方法可用于断言响应具有以 JSON 形式返回的验证错误,或者错误已被闪存到会话存储中。

assertJsonValidationErrorFor

断言响应在给定键下有任何 JSON 验证错误:

$response->assertJsonValidationErrorFor(string $key, $responseKey = 'errors');

assertMethodNotAllowed

断言响应具有 method not allowed(405)HTTP 状态码:

$response->assertMethodNotAllowed();

assertMovedPermanently

断言响应具有 moved permanently(301)HTTP 状态码:

$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

断言响应具有 not found(404)HTTP 状态码:

$response->assertNotFound();

assertOk

断言响应具有 200 HTTP 状态码:

$response->assertOk();

assertPaymentRequired

断言响应具有 payment required(402)HTTP 状态码:

$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

断言响应是否具有请求超时(408)HTTP 状态码:

$response->assertRequestTimeout();

assertSee

断言给定字符串是否包含在响应中。除非第二个参数传入 false,否则此断言会自动转义给定字符串:

$response->assertSee($value, $escape = true);

assertSeeInOrder

断言给定字符串是否按顺序包含在响应中。除非第二个参数传入 false,否则此断言会自动转义给定字符串:

$response->assertSeeInOrder(array $values, $escape = true);

assertSeeText

断言给定字符串是否包含在响应文本中。除非第二个参数传入 false,否则此断言会自动转义给定字符串。在执行断言之前,响应内容会被传递给 strip_tags PHP 函数:

$response->assertSeeText($value, $escape = true);

assertSeeTextInOrder

断言给定字符串是否按顺序包含在响应文本中。除非第二个参数传入 false,否则此断言会自动转义给定字符串。在执行断言之前,响应内容会被传递给 strip_tags PHP 函数:

$response->assertSeeTextInOrder(array $values, $escape = true);

assertServerError

断言响应是否具有服务器错误(>= 500 ,< 600)HTTP 状态码:

$response->assertServerError();

assertServiceUnavailable

断言响应是否具有“服务不可用”(503)HTTP 状态码:

$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.'
]);

更通用的 assertInvalid 方法可用于断言响应以 JSON 格式返回验证错误 或者错误已闪存到会话存储。

assertSessionHasErrorsIn

断言会话在特定的错误包中是否包含给定 $keys 的错误。如果 $keys 是关联数组,则断言会话在错误包中每个字段(键)包含特定的错误信息(值):

$response->assertSessionHasErrorsIn($errorBag, $keys = [], $format = null);

assertSessionHasNoErrors

断言会话没有验证错误:

$response->assertSessionHasNoErrors();

assertSessionDoesntHaveErrors

断言会话中给定键没有验证错误:

$response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default');

更通用的 assertValid 方法可用于断言响应没有以 JSON 格式返回验证错误 且没有错误闪存到会话存储。

assertSessionMissing

断言会话不包含给定键:

$response->assertSessionMissing($key);

assertSessionMissingInput

断言会话在闪存输入数组中缺少给定输入键:

$response->assertSessionMissingInput($key);

assertStatus

断言响应具有给定的 HTTP 状态码:

$response->assertStatus($code);

assertSuccessful

断言响应具有成功(>= 200 且 < 300)的 HTTP 状态码:

$response->assertSuccessful();

assertTooManyRequests

断言响应具有请求过多(429)HTTP 状态码:

$response->assertTooManyRequests();

assertUnauthorized

断言响应具有未授权(401)HTTP 状态码:

$response->assertUnauthorized();

assertUnprocessable

断言响应具有无法处理实体(422)HTTP 状态码:

$response->assertUnprocessable();

assertUnsupportedMediaType

断言响应具有不支持的媒体类型(415)HTTP 状态码:

$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' => '[email protected],',
]);

assertViewIs

断言给定视图由路由返回:

$response->assertViewIs($value);

assertViewMissing

断言给定的数据键未提供给应用响应中返回的视图:

$response->assertViewMissing($key);

身份验证断言

Laravel 还提供了多种与身份验证相关的断言,可在应用的功能测试中使用。请注意,这些方法在测试类本身上调用,而不是在 Illuminate\Testing\TestResponse 实例上调用,该实例由 get 和 post 等方法返回。

assertAuthenticated

断言用户已通过身份验证:

$this->assertAuthenticated($guard = null);

assertGuest

断言用户未通过身份验证:

$this->assertGuest($guard = null);

assertAuthenticatedAs

断言特定用户已通过身份验证:

$this->assertAuthenticatedAs($user, $guard = null);

验证断言

Laravel 提供两个主要的与验证相关的断言,可用于确保请求中提供的数据是有效的或无效的。

assertValid

断言响应在给定键上没有验证错误。此方法可用于对验证错误以 JSON 结构返回或验证错误已闪存到会话中的响应进行断言:

// Assert that no validation errors are present...
$response->assertValid();

// Assert that the given keys do not have validation errors...
$response->assertValid(['name', 'email']);

assertInvalid

断言响应在给定键上存在验证错误。此方法可用于对验证错误以 JSON 结构返回或验证错误已闪存到会话中的响应进行断言:

$response->assertInvalid(['name', 'email']);

您也可以断言给定键具有特定的验证错误消息。执行此操作时,您可以提供完整的消息,也可以只提供消息的一部分:

$response->assertInvalid([
    'name' => 'The name field is required.',
    'email' => 'valid email address',
]);