Skip to content

HTTP 测试

简介

Laravel 为向应用发起 HTTP 请求并检查响应提供了非常流畅的 API。下面来看一个功能测试示例:

php
<?php

test('the application returns a successful response', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_the_application_returns_a_successful_response(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

get 方法会向应用发起一个 GET 请求,而 assertStatus 方法会断言返回响应具有给定的 HTTP 状态码。除了这个简单断言之外,Laravel 还提供了大量断言,用于检查响应头、内容、JSON 结构等。

发起请求

要向应用发起请求,你可以在测试中调用 getpostputpatchdelete 方法。这些方法并不会真正向应用发起“真实”的 HTTP 请求,整个网络请求过程是在内部模拟完成的。

测试请求方法返回的不是 Illuminate\Http\Response 实例,而是 Illuminate\Testing\TestResponse 实例。它提供了大量有用断言,让你能够检查应用的响应:

php
<?php

test('basic request', function () {
    $response = $this->get('/');

    $response->assertStatus(200);
});
php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_a_basic_request(): void
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

通常来说,每个测试只应向应用发起一次请求。如果在单个测试方法里执行多个请求,可能会出现意外行为。

NOTE

为了方便,在运行测试时,CSRF middleware 会被自动禁用。

自定义请求头

你可以在请求发送到应用之前,使用 withHeaders 方法自定义请求头。这个方法允许你向请求添加任意自定义 header:

php
<?php

test('interacting with headers', function () {
    $response = $this->withHeaders([
        'X-Header' => 'Value',
    ])->post('/user', ['name' => 'Sally']);

    $response->assertStatus(201);
});
php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_interacting_with_headers(): void
    {
        $response = $this->withHeaders([
            'X-Header' => 'Value',
        ])->post('/user', ['name' => 'Sally']);

        $response->assertStatus(201);
    }
}

Cookie

你可以在发起请求前使用 withCookiewithCookies 方法设置 cookie 值。withCookie 方法接收 cookie 名称和值两个参数,而 withCookies 方法接收一个名称 / 值数组:

php
<?php

test('interacting with cookies', function () {
    $response = $this->withCookie('color', 'blue')->get('/');

    $response = $this->withCookies([
        'color' => 'blue',
        'name' => 'Taylor',
    ])->get('/');

    //
});
php
<?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('/');

        //
    }
}

Session / 认证

Laravel 提供了多个辅助方法,用于在 HTTP 测试期间与 session 交互。首先,你可以使用 withSession 方法将 session 数据设置为指定数组。这对于在向应用发起请求前预先填充 session 很有用:

php
<?php

test('interacting with the session', function () {
    $response = $this->withSession(['banned' => false])->get('/');

    //
});
php
<?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 的 session 通常用于维护当前已认证用户的状态。因此,actingAs 辅助方法提供了一种简单方式,把给定用户作为当前用户进行认证。例如,你可以使用模型工厂来生成并认证一个用户:

php
<?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
<?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:

php
$this->actingAs($user, 'web');

如果你想确保请求处于未认证状态,可以使用 actingAsGuest 方法:

php
$this->actingAsGuest();

调试响应

向应用发起测试请求后,你可以使用 dumpdumpHeadersdumpSession 方法查看并调试响应内容:

php
<?php

test('basic test', function () {
    $response = $this->get('/');

    $response->dump();
    $response->dumpHeaders();
    $response->dumpSession();
});
php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_basic_test(): void
    {
        $response = $this->get('/');

        $response->dump();
        $response->dumpHeaders();
        $response->dumpSession();
    }
}

或者,你也可以使用 ddddHeadersddBodyddJsonddSession 方法,在输出响应信息后立即停止执行:

php
<?php

test('basic test', function () {
    $response = $this->get('/');

    $response->dd();
    $response->ddHeaders();
    $response->ddBody();
    $response->ddJson();
    $response->ddSession();
});
php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    public function test_basic_test(): void
    {
        $response = $this->get('/');

        $response->dd();
        $response->ddHeaders();
        $response->ddBody();
        $response->ddJson();
        $response->ddSession();
    }
}

异常处理

有时你可能需要测试应用是否抛出了某个特定异常。为此,你可以通过 Exceptions facade “伪造”异常处理器。异常处理器被伪造后,就可以使用 assertReportedassertNotReported 方法,对请求期间抛出的异常执行断言:

php
<?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
<?php

namespace Tests\Feature;

use App\Exceptions\InvalidOrderException;
use Illuminate\Support\Facades\Exceptions;
use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic test example.
     */
    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.';
        });
    }
}

assertNotReportedassertNothingReported 方法可用于断言请求期间某个给定异常未被抛出,或根本没有任何异常被抛出:

php
Exceptions::assertNotReported(InvalidOrderException::class);

Exceptions::assertNothingReported();

你可以在发起请求前调用 withoutExceptionHandling 方法,完全禁用该请求的异常处理:

php
$response = $this->withoutExceptionHandling()->get('/');

此外,如果你想确保应用没有使用 PHP 语言或应用依赖库中已经废弃的特性,可以在发起请求前调用 withoutDeprecationHandling 方法。禁用废弃处理后,废弃警告会被转换为异常,从而导致测试失败:

php
$response = $this->withoutDeprecationHandling()->get('/');

assertThrows 方法可用于断言某个闭包中的代码会抛出指定类型的异常:

php
$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    OrderInvalid::class
);

如果你想检查并对抛出的异常执行断言,可以将闭包作为 assertThrows 方法的第二个参数:

php
$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    fn (OrderInvalid $e) => $e->orderId() === 123;
);

assertDoesntThrow 方法可用于断言某个闭包中的代码不会抛出任何异常:

php
$this->assertDoesntThrow(fn () => (new ProcessOrder)->execute());

测试 JSON API

Laravel 还提供了多个辅助方法,用于测试 JSON API 及其响应。例如,jsongetJsonpostJsonputJsonpatchJsondeleteJsonoptionsJson 方法可用于使用不同 HTTP 动词发起 JSON 请求。你还可以轻松地向这些方法传入数据和请求头。下面从一个测试开始:向 /api/user 发起 POST 请求,并断言返回了预期 JSON 数据:

php
<?php

test('making an api request', function () {
    $response = $this->postJson('/api/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertJson([
            'created' => true,
        ]);
});
php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_making_an_api_request(): void
    {
        $response = $this->postJson('/api/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJson([
                'created' => true,
            ]);
    }
}

此外,你也可以像数组一样读取响应中的 JSON 数据,这样可以方便地检查 JSON 响应里的单个值:

php
expect($response['created'])->toBeTrue();
php
$this->assertTrue($response['created']);

NOTE

assertJson 方法会先把响应转换为数组,再验证给定数组是否存在于应用返回的 JSON 响应中。因此,只要给定片段存在,即使 JSON 响应里还有其他属性,这个测试也会通过。

断言 JSON 完全匹配

正如前面所说,assertJson 方法可用于断言 JSON 响应中存在某个片段。如果你想验证某个数组与应用返回的 JSON 完全一致,则应使用 assertExactJson 方法:

php
<?php

test('asserting an exact json match', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertExactJson([
            'created' => true,
        ]);
});
php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    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
<?php

test('asserting a json path value', function () {
    $response = $this->postJson('/user', ['name' => 'Sally']);

    $response
        ->assertStatus(201)
        ->assertJsonPath('team.owner.name', 'Darian');
});
php
<?php

namespace Tests\Feature;

use Tests\TestCase;

class ExampleTest extends TestCase
{
    /**
     * A basic functional test example.
     */
    public function test_asserting_a_json_paths_value(): void
    {
        $response = $this->postJson('/user', ['name' => 'Sally']);

        $response
            ->assertStatus(201)
            ->assertJsonPath('team.owner.name', 'Darian');
    }
}

assertJsonPath 方法还接受一个闭包,可用于动态判断断言是否应通过:

php
$response->assertJsonPath('team.owner.name', fn (string $name) => strlen($name) >= 3);

流式 JSON 测试

Laravel 还提供了一种优雅方式,以流式风格测试应用的 JSON 响应。首先,把一个闭包传给 assertJson 方法。该闭包会收到一个 Illuminate\Testing\Fluent\AssertableJson 实例,你可以用它对应用返回的 JSON 执行断言。where 方法可用于对 JSON 某个属性执行断言,而 missing 方法可用于断言某个属性不存在:

php
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()
        );
});
php
use Illuminate\Testing\Fluent\AssertableJson;

/**
 * A basic functional test example.
 */
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 响应中无意暴露敏感信息。Laravel 会强制你要么显式对某个属性做断言,要么通过 etc 方法显式允许出现额外属性。

不过你也要注意,不在断言链中使用 etc 方法,并不意味着 JSON 对象内部嵌套数组中的额外属性也会被自动禁止。etc 方法只确保在它被调用的那个嵌套层级中不存在额外属性。

断言属性存在 / 不存在

要断言某个属性存在或不存在,可以使用 hasmissing 方法:

php
$response->assertJson(fn (AssertableJson $json) =>
    $json->has('data')
        ->missing('message')
);

此外,hasAllmissingAll 方法允许你一次性断言多个属性存在或不存在:

php
$response->assertJson(fn (AssertableJson $json) =>
    $json->hasAll(['status', 'data'])
        ->missingAll(['message', 'code'])
);

你可以使用 hasAny 方法,判断给定属性列表中是否至少有一个属性存在:

php
$response->assertJson(fn (AssertableJson $json) =>
    $json->has('status')
        ->hasAny('data', 'message', 'code')
);

对 JSON 集合执行断言

很多时候,你的路由会返回包含多个项目的 JSON 响应,例如多个用户:

php
Route::get('/users', function () {
    return User::all();
});

在这种情况下,我们可以使用流式 JSON 对象的 has 方法,对响应中包含的用户执行断言。例如,下面断言 JSON 响应包含三个用户。接着,使用 first 方法对集合中的第一个用户做一些断言。first 方法接受一个闭包,该闭包会收到另一个可断言 JSON 对象实例:

php
$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 方法:

php
$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 集合:

php
Route::get('/users', function () {
    return [
        'meta' => [...],
        'users' => User::all(),
    ];
})

测试这些路由时,你可以使用 has 方法断言集合中的项目数量。此外,你也可以使用 has 方法来限定断言链的作用域:

php
$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 方法来检查 users 集合,你也可以只调用一次,并将闭包作为第三个参数传入。此时,这个闭包会自动在集合第一项的作用域内执行:

php
$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 类提供了 whereTypewhereAllType 方法来完成此事:

php
$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('id', 'integer')
        ->whereAllType([
            'users.0.name' => 'string',
            'meta' => 'array'
        ])
);

你可以在 whereType 的第二个参数中使用 | 字符指定多个类型,或者传入一个类型数组。只要响应值属于列出的任意类型,断言就会通过:

php
$response->assertJson(fn (AssertableJson $json) =>
    $json->whereType('name', 'string|null')
        ->whereType('id', ['string', 'integer'])
);

whereTypewhereAllType 支持以下类型:stringintegerdoublebooleanarraynull

测试文件上传

Illuminate\Http\UploadedFile 类提供了 fake 方法,可用于为测试生成虚拟文件或图片。配合 Storage facade 的 fake 方法,文件上传测试会变得非常简单。例如,你可以结合这两个特性,轻松测试头像上传表单:

php
<?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
<?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 方法:

php
Storage::fake('avatars');

// ...

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

自定义虚拟文件

通过 UploadedFile 类的 fake 方法创建文件时,你可以指定图片的宽度、高度和大小(单位 KB),从而更好地测试应用的验证规则:

php
UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);

除了创建图片外,你还可以使用 create 方法创建其他任意类型的文件:

php
UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);

如果需要,你可以向该方法传入 $mimeType 参数,显式定义文件应返回的 MIME 类型:

php
UploadedFile::fake()->create(
    'document.pdf', $sizeInKilobytes, 'application/pdf'
);

测试视图

Laravel 还允许你在不模拟 HTTP 请求的情况下直接渲染视图。为此,你可以在测试中调用 view 方法。view 方法接受视图名称和一个可选数据数组,并返回 Illuminate\Testing\TestView 实例。该实例提供了多个方便的方法,用于对视图内容执行断言:

php
<?php

test('a welcome view can be rendered', function () {
    $view = $this->view('welcome', ['name' => 'Taylor']);

    $view->assertSee('Taylor');
});
php
<?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 类提供以下断言方法:assertSeeassertSeeInOrderassertSeeTextassertSeeTextInOrderassertDontSeeassertDontSeeText

如果需要,你可以把 TestView 实例转换为字符串,从而获取原始渲染后的视图内容:

php
$contents = (string) $this->view('welcome');

共享错误信息

某些视图可能依赖于 Laravel 提供的全局错误包中共享的错误。要为错误包填充错误消息,可以使用 withViewErrors 方法:

php
$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 实例:

php
$view = $this->blade(
    '<x-component :name="$name" />',
    ['name' => 'Taylor']
);

$view->assertSee('Taylor');

你还可以使用 component 方法求值并渲染 Blade 组件component 方法会返回 Illuminate\Testing\TestComponent 实例:

php
$view = $this->component(Profile::class, ['name' => 'Taylor']);

$view->assertSee('Taylor');

缓存路由

在测试运行前,Laravel 会启动一个全新的应用实例,包括收集所有已定义路由。如果你的应用拥有大量路由文件,可以考虑在测试用例中加入 Illuminate\Foundation\Testing\WithCachedRoutes trait。使用该 trait 的测试会只构建一次路由并缓存到内存中,这意味着整个测试套件只会执行一次路由收集过程:

php
<?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
<?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 类提供了大量自定义断言方法,可在测试应用时使用。这些断言可以直接在由 jsongetpostputdelete 等测试方法返回的响应对象上调用:

assertAccepted

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

php
$response->assertAccepted();

assertBadRequest

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

php
$response->assertBadRequest();

assertClientError

断言响应具有 client error(>= 400 且 < 500)HTTP 状态码:

php
$response->assertClientError();

assertConflict

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

php
$response->assertConflict();

断言响应包含给定 cookie:

php
$response->assertCookie($cookieName, $value = null);

断言响应包含给定 cookie,且该 cookie 已过期:

php
$response->assertCookieExpired($cookieName);

断言响应包含给定 cookie,且该 cookie 尚未过期:

php
$response->assertCookieNotExpired($cookieName);

断言响应不包含给定 cookie:

php
$response->assertCookieMissing($cookieName);

assertCreated

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

php
$response->assertCreated();

assertDontSee

断言应用返回的响应中不包含给定字符串。除非你将第二个参数显式传为 false,否则该断言会自动对给定字符串做转义:

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

assertDontSeeText

断言响应文本中不包含给定字符串。除非你将第二个参数显式传为 false,否则该断言会自动对给定字符串做转义。执行断言之前,该方法会先将响应内容传给 strip_tags PHP 函数:

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

assertDownload

断言响应是一个“下载”响应。通常这意味着被调用的路由返回的是 Response::download 响应、BinaryFileResponse 响应,或 Storage::download 响应:

php
$response->assertDownload();

如果你愿意,也可以断言被下载的文件具有给定文件名:

php
$response->assertDownload('image.jpg');

assertExactJson

断言响应中的 JSON 数据与给定 JSON 数据完全一致:

php
$response->assertExactJson(array $data);

assertExactJsonStructure

断言响应中的 JSON 结构与给定结构完全一致:

php
$response->assertExactJsonStructure(array $data);

这个方法比 assertJsonStructure 更严格。与 assertJsonStructure 不同,如果响应中包含任何未在预期 JSON 结构中显式列出的键,该方法就会失败。

assertForbidden

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

php
$response->assertForbidden();

assertFound

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

php
$response->assertFound();

assertGone

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

php
$response->assertGone();

assertHeader

断言响应中存在给定 header 及其值:

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

assertHeaderContains

断言给定 header 包含指定子串值:

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

assertHeaderMissing

断言响应中不存在给定 header:

php
$response->assertHeaderMissing($headerName);

assertInternalServerError

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

php
$response->assertInternalServerError();

assertJson

断言响应包含给定 JSON 数据:

php
$response->assertJson(array $data, $strict = false);

assertJson 方法会将响应转换为数组,以验证给定数组是否存在于应用返回的 JSON 响应中。因此,只要给定片段存在,即使 JSON 响应中还有其他属性,测试也会通过。

assertJsonCount

断言响应 JSON 在指定键处的数组拥有预期数量的项目:

php
$response->assertJsonCount($count, $key = null);

assertJsonFragment

断言响应中任意位置包含给定 JSON 数据:

php
Route::get('/users', function () {
    return [
        'users' => [
            [
                'name' => 'Taylor Otwell',
            ],
        ],
    ];
});

$response->assertJsonFragment(['name' => 'Taylor Otwell']);

assertJsonIsArray

断言响应 JSON 是数组:

php
$response->assertJsonIsArray();

assertJsonIsObject

断言响应 JSON 是对象:

php
$response->assertJsonIsObject();

assertJsonMissing

断言响应不包含给定 JSON 数据:

php
$response->assertJsonMissing(array $data);

assertJsonMissingExact

断言响应不包含完全匹配的给定 JSON 数据:

php
$response->assertJsonMissingExact(array $data);

assertJsonMissingValidationErrors

断言响应中给定键不存在 JSON 验证错误:

php
$response->assertJsonMissingValidationErrors($keys);

NOTE

更通用的 assertValid 方法可用于断言:响应中不存在以 JSON 形式返回的验证错误,且 session 中也没有被 flash 的错误信息。

assertJsonPath

断言响应在指定路径处包含给定数据:

php
$response->assertJsonPath($path, $expectedValue);

例如,如果应用返回如下 JSON 响应:

json
{
    "user": {
        "name": "Steve Schoger"
    }
}

你可以像下面这样断言 user 对象的 name 属性匹配给定值:

php
$response->assertJsonPath('user.name', 'Steve Schoger');

assertJsonMissingPath

断言响应中不存在给定路径:

php
$response->assertJsonMissingPath($path);

例如,如果应用返回如下 JSON 响应:

json
{
    "user": {
        "name": "Steve Schoger"
    }
}

你可以断言其中不存在 user 对象的 email 属性:

php
$response->assertJsonMissingPath('user.email');

assertJsonStructure

断言响应拥有给定的 JSON 结构:

php
$response->assertJsonStructure(array $structure);

例如,如果应用返回的 JSON 响应包含以下数据:

json
{
    "user": {
        "name": "Steve Schoger"
    }
}

你可以像下面这样断言 JSON 结构符合预期:

php
$response->assertJsonStructure([
    'user' => [
        'name',
    ]
]);

有时,应用返回的 JSON 响应可能包含对象数组:

json
{
    "user": [
        {
            "name": "Steve Schoger",
            "age": 55,
            "location": "Earth"
        },
        {
            "name": "Mary Schoger",
            "age": 60,
            "location": "Earth"
        }
    ]
}

这种情况下,你可以使用 * 字符来断言数组中所有对象的结构:

php
$response->assertJsonStructure([
    'user' => [
        '*' => [
             'name',
             'age',
             'location'
        ]
    ]
]);

assertJsonValidationErrors

断言响应中针对给定键存在指定 JSON 验证错误。当验证错误是以 JSON 结构返回,而不是 flash 到 session 时,应使用此方法:

php
$response->assertJsonValidationErrors(array $data, $responseKey = 'errors');

NOTE

更通用的 assertInvalid 方法可用于断言:响应中的验证错误可能以 JSON 返回,或者已被 flash 到 session。

assertJsonValidationErrorFor

断言响应中针对给定键存在任意 JSON 验证错误:

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

assertMethodNotAllowed

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

php
$response->assertMethodNotAllowed();

assertMovedPermanently

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

php
$response->assertMovedPermanently();

assertLocation

断言响应的 Location header 具有给定 URI 值:

php
$response->assertLocation($uri);

assertContent

断言给定字符串与响应内容完全匹配:

php
$response->assertContent($value);

assertNoContent

断言响应具有给定 HTTP 状态码,且没有任何内容:

php
$response->assertNoContent($status = 204);

assertStreamed

断言响应是流式响应:

php
$response->assertStreamed();

assertStreamedContent

断言给定字符串与流式响应内容匹配:

php
$response->assertStreamedContent($value);

assertNotFound

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

php
$response->assertNotFound();

assertOk

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

php
$response->assertOk();

assertPaymentRequired

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

php
$response->assertPaymentRequired();

断言响应包含给定未加密 cookie:

php
$response->assertPlainCookie($cookieName, $value = null);

assertRedirect

断言响应重定向到了给定 URI:

php
$response->assertRedirect($uri = null);

assertRedirectBack

断言响应是否重定向回上一页:

php
$response->assertRedirectBack();

assertRedirectBackWithErrors

断言响应是否重定向回上一页,并且 session 中存在给定错误

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

assertRedirectBackWithoutErrors

断言响应是否重定向回上一页,且 session 中不存在任何错误消息:

php
$response->assertRedirectBackWithoutErrors();

assertRedirectContains

断言响应重定向到的 URI 包含给定字符串:

php
$response->assertRedirectContains($string);

assertRedirectToRoute

断言响应重定向到了给定命名路由

php
$response->assertRedirectToRoute($name, $parameters = []);

assertRedirectToSignedRoute

断言响应重定向到了给定签名路由

php
$response->assertRedirectToSignedRoute($name = null, $parameters = []);

assertRequestTimeout

断言响应具有 request timeout(408)HTTP 状态码:

php
$response->assertRequestTimeout();

assertSee

断言响应中包含给定字符串。除非你将第二个参数显式传为 false,否则该断言会自动对给定字符串做转义:

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

assertSeeInOrder

断言响应中按顺序包含给定字符串数组。除非你将第二个参数显式传为 false,否则这些字符串会被自动转义:

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

assertSeeText

断言响应文本中包含给定字符串。除非你将第二个参数显式传为 false,否则该断言会自动对给定字符串做转义。断言前会先将响应内容传给 strip_tags PHP 函数:

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

assertSeeTextInOrder

断言响应文本中按顺序包含给定字符串数组。除非你将第二个参数显式传为 false,否则这些字符串会被自动转义。断言前会先将响应内容传给 strip_tags PHP 函数:

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

assertServerError

断言响应具有 server error(>= 500 且 < 600)HTTP 状态码:

php
$response->assertServerError();

assertServiceUnavailable

断言响应具有 “Service Unavailable”(503)HTTP 状态码:

php
$response->assertServiceUnavailable();

assertSessionHas

断言 session 中包含给定数据:

php
$response->assertSessionHas($key, $value = null);

如果需要,你也可以将闭包作为 assertSessionHas 方法的第二个参数传入。只要闭包返回 true,断言就会通过:

php
$response->assertSessionHas($key, function (User $value) {
    return $value->name === 'Taylor Otwell';
});

assertSessionHasInput

断言 session 的 flash 输入数组中包含给定值:

php
$response->assertSessionHasInput($key, $value = null);

如果需要,你也可以将闭包作为 assertSessionHasInput 方法的第二个参数传入。只要闭包返回 true,断言就会通过:

php
use Illuminate\Support\Facades\Crypt;

$response->assertSessionHasInput($key, function (string $value) {
    return Crypt::decryptString($value) === 'secret';
});

assertSessionHasAll

断言 session 中包含给定的键 / 值数组:

php
$response->assertSessionHasAll(array $data);

例如,如果应用的 session 中包含 namestatus 键,你可以像下面这样断言它们同时存在且值匹配:

php
$response->assertSessionHasAll([
    'name' => 'Taylor Otwell',
    'status' => 'active',
]);

assertSessionHasErrors

断言 session 中包含给定 $keys 的错误。如果 $keys 是关联数组,则断言 session 中每个字段(键)都包含特定错误消息(值)。当你测试的是把验证错误 flash 到 session,而不是以 JSON 结构返回的路由时,应使用这个方法:

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

例如,要断言 nameemail 字段的验证错误消息已被 flash 到 session,可以这样调用 assertSessionHasErrors

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

或者,你也可以断言某个字段具有特定验证错误消息:

php
$response->assertSessionHasErrors([
    'name' => 'The given name was invalid.'
]);

NOTE

更通用的 assertInvalid 方法可用于断言:响应中的验证错误可能以 JSON 返回,或者已被 flash 到 session。

assertSessionHasErrorsIn

断言 session 中在某个特定错误包里包含给定 $keys 的错误。如果 $keys 是关联数组,则断言在该错误包内,每个字段(键)都有指定错误消息(值):

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

assertSessionHasNoErrors

断言 session 中不存在验证错误:

php
$response->assertSessionHasNoErrors();

assertSessionDoesntHaveErrors

断言 session 中给定键不存在验证错误:

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

NOTE

更通用的 assertValid 方法可用于断言:响应中不存在以 JSON 形式返回的验证错误,且 session 中也没有被 flash 的错误信息。

assertSessionMissing

断言 session 中不包含给定键:

php
$response->assertSessionMissing($key);

assertStatus

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

php
$response->assertStatus($code);

assertSuccessful

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

php
$response->assertSuccessful();

assertTooManyRequests

断言响应具有 too many requests(429)HTTP 状态码:

php
$response->assertTooManyRequests();

assertUnauthorized

断言响应具有 unauthorized(401)HTTP 状态码:

php
$response->assertUnauthorized();

assertUnprocessable

断言响应具有 unprocessable entity(422)HTTP 状态码:

php
$response->assertUnprocessable();

assertUnsupportedMediaType

断言响应具有 unsupported media type(415)HTTP 状态码:

php
$response->assertUnsupportedMediaType();

assertValid

断言响应在给定键上不存在验证错误。这个方法既可用于验证错误以 JSON 结构返回的响应,也可用于验证错误被 flash 到 session 的响应:

php
// 断言不存在任何验证错误...
$response->assertValid();

// 断言给定键不存在验证错误...
$response->assertValid(['name', 'email']);

assertInvalid

断言响应在给定键上存在验证错误。这个方法既可用于验证错误以 JSON 结构返回的响应,也可用于验证错误被 flash 到 session 的响应:

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

你也可以断言给定键具有某条特定验证错误消息。此时,你既可以传入完整消息,也可以只传消息的一部分:

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

如果你想断言只有给定字段存在验证错误,可以使用 assertOnlyInvalid 方法:

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

assertViewHas

断言响应视图中包含给定数据:

php
$response->assertViewHas($key, $value = null);

将闭包作为 assertViewHas 方法的第二个参数传入,可以让你检查并断言某个具体视图数据:

php
$response->assertViewHas('user', function (User $user) {
    return $user->name === 'Taylor';
});

此外,你也可以像数组变量一样访问响应中的视图数据,从而方便地检查它:

php
expect($response['name'])->toBe('Taylor');
php
$this->assertEquals('Taylor', $response['name']);

assertViewHasAll

断言响应视图中包含给定数据列表:

php
$response->assertViewHasAll(array $data);

这个方法可以用来断言视图中仅仅存在这些数据键:

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

或者,你也可以断言这些视图数据存在且具有特定值:

php
$response->assertViewHasAll([
    'name' => 'Taylor Otwell',
    'email' => 'taylor@example.com,',
]);

assertViewIs

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

php
$response->assertViewIs($value);

assertViewMissing

断言应用响应所返回的视图中,不存在指定数据键:

php
$response->assertViewMissing($key);

认证断言

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

assertAuthenticated

断言某个用户已通过认证:

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

assertGuest

断言某个用户未通过认证:

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

assertAuthenticatedAs

断言指定用户已通过认证:

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

验证断言

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

assertValid

断言响应在给定键上不存在验证错误。这个方法既可用于验证错误以 JSON 结构返回的响应,也可用于验证错误被 flash 到 session 的响应:

php
// 断言不存在任何验证错误...
$response->assertValid();

// 断言给定键不存在验证错误...
$response->assertValid(['name', 'email']);

assertInvalid

断言响应在给定键上存在验证错误。这个方法既可用于验证错误以 JSON 结构返回的响应,也可用于验证错误被 flash 到 session 的响应:

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

你也可以断言某个键具有特定验证错误消息。此时,你既可以提供完整消息,也可以只提供其中一小部分:

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