HTTP 测试

简介

Laravel 为向应用发起 HTTP 请求并检查输出提供了非常流畅的 API。例如,看一个如下定义的测试:

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
    /**
     * 一个基本的测试示例
     *
     * @return void
     */
    public function testBasicTest()
    {
        $response = $this->get('/');

        $response->assertStatus(200);
    }
}

get 方法像应用发起 GET 请求,而 assertStatus 方法断言返回的响应应该有给定的 HTTP 状态码。除了以上简单的断言外,Laravel 也包含检查响应头,内容,JSON 结构等各种断言。

自定义请求头

可以使用 withHeaders 方法在请求发送给应用前自定义请求头。可以在请求中添加任何要自定义的请求头:

class ExampleTest extends TestCase
{
    /**
     * 一个基本功能的测试示例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->withHeaders([
            'X-Header' => 'Value',
        ])->json('POST', '/user', ['name' => 'Sally']);

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

运行测试时,CSRF 中间件会自动禁用。

会话/认证

Laravel 提供了几个辅助函数在 HTTP 测试时处理会话。首先,可以使用 withSession 方法将会话数据设置为给定数组。这在向应用发送请求前在会话中添加数据时很有用:

class ExampleTest extends TestCase
{
    public function testApplication()
    {
        $response = $this->withSession(['foo' => 'bar'])
                         ->get('/');
    }
}

当然,会话一个常见的用法是维护认证用户的状态。actingAs 辅助方法提供了一个简单的方法将给定用户认证为当前用户。例如,我们可以使用 模型工厂 生成并授权用户:

use App\User;

class ExampleTest extends TestCase
{
    public function testApplication()
    {
        $user = factory(User::class)->create();

        $response = $this->actingAs($user)
                         ->withSession(['foo' => 'bar'])
                         ->get('/');
    }
}

还可以通过将看守器名称作为第二个参数传递给 actingAs 方法指定认证给定用户时使用的看守器:

$this->actingAs($user, 'api')

测试 JSON API

Laravel 也提供了几个辅助方法用于测试 JSON API 以及其响应。例如,jsongetpostputpatchdelete 方法可用于发起各种类型的 HTTP 请求。还可以在这些方法中轻松传递数据和请求头。首先,我们编写一个测试,向 /user 发起一个 POST 请求并断言期望返回的数据:

class ExampleTest extends TestCase
{
    /**
     * 一个基本功能的测试示例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->json('POST', '/user', ['name' => 'Sally']);

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

assertJson 方法将响应转换为数组,并使用 PHPUnit::assertArraySubset 验证给定数组存在于应用返回的 JSON 响应中。因此,如果 JSON 响应中还有其它属性,只要给定属性片段存在测试仍会通过。

验证 JSON 完全匹配

如果要验证给定数组和应用返回的 JSON 完全 匹配,可以使用 assertExactJson 方法:

class ExampleTest extends TestCase
{
    /**
     * 一个基本功能的测试示例
     *
     * @return void
     */
    public function testBasicExample()
    {
        $response = $this->json('POST', '/user', ['name' => 'Sally']);

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

测试文件上传

Illuminate\Http\UploadedFile 类提供了一个 fake 方法,可用于生成模拟文件或图片进行测试。结合 Storage Facade 的 fake 方法极大的简化了文件上传测试。例如,我们可以结合这两个功能轻松测试头像上传表单:

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Storage;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithoutMiddleware;

class ExampleTest extends TestCase
{
    public function testAvatarUpload()
    {
        Storage::fake('avatars');

        $file = UploadedFile::fake()->image('avatar.jpg');

        $response = $this->json('POST', '/avatar', [
            'avatar' => $file,
        ]);

        // 断言文件存储了
        Storage::disk('avatars')->assertExists($file->hashName());

        // 断言文件不存在
        Storage::disk('avatars')->assertMissing('missing.jpg');
    }
}

自定义模拟文件

使用 fake 方法创建文件时,可以指定图片的宽、高和大小来更好地测试验证规则:

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

除了创建图片,还可以使用 create 方法创建任何其它类型的文件:

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

可用的断言方法

断言响应

Laravel 为 PHPUnit 测试提供了各种自定义断言方法。这些断言可以在 jsongetpostputdelete 测试方法返回的响应上使用:

assertCookie
assertCookieExpired
assertCookieNotExpired
assertCookieMissing
assertDontSee
assertDontSeeText
assertExactJson
assertForbidden
assertHeader
assertHeaderMissing
assertJson
assertJsonCount
assertJsonFragment
assertJsonMissing
assertJsonMissingExact
assertJsonStructure
assertJsonValidationErrors
assertLocation
assertNotFound
assertOk
assertPlainCookie
assertRedirect
assertSee
assertSeeInOrder
assertSeeText
assertSeeTextInOrder
assertSessionHas
assertSessionHasAll
assertSessionHasErrors
assertSessionHasErrorsIn
assertSessionHasNoErrors
assertSessionMissing
assertStatus
assertSuccessful
assertViewHas
assertViewHasAll
assertViewIs
assertViewMissing

assertCookie

断言响应包含给定 Cookie:

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

assertCookieExpired

断言响应包含给定 Cookie 并且过期了:

$response->assertCookieExpired($cookieName);

assertCookieNotExpired

断言响应包含给定 Cookie 但没有过期:

$response->assertCookieNotExpired($cookieName);

assertCookieMissing

断言响应不包含给定 Cookie:

$response->assertCookieMissing($cookieName);

assertDontSee

断言给定字符串不包含在响应中:

$response->assertDontSee($value);

assertDontSeeText

断言给定字符串不包含在响应文本中:

$response->assertDontSeeText($value);

assertExactJson

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

$response->assertExactJson(array $data);

assertForbidden

断言响应有 403 状态码:

$response->assertForbidden();

assertHeader

断言给定响应头在响应中:

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

assertHeaderMissing

断言给定响应头不在响应中:

$response->assertHeaderMissing($headerName);

assertJson

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

$response->assertJson(array $data);

assertJsonCount

断言响应 JSON 的数组元素个数是给定键:

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

assertJsonFragment

断言响应包含给定的 JSON 片段:

$response->assertJsonFragment(array $data);

assertJsonMissing

断言响应不包含给定 JSON 片段:

$response->assertJsonMissing(array $data);

assertJsonMissingExact

断言响应不包含完整的 JSON 片段:

$response->assertJsonMissingExact(array $data);

assertJsonStructure

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

$response->assertJsonStructure(array $structure);

assertJsonValidationErrors

断言响应有给定键的 JSON 验证错误:

$response->assertJsonValidationErrors($keys);

assertLocation

断言响应在 Location 响应头中有给定 URL 值:

$response->assertLocation($uri);

assertNotFound

断言响应有 404 状态码:

$response->assertNotFound();

assertOk

断言响应有 200 状态码:

$response->assertOk();

assertPlainCookie

断言响应包含给定 Cookie(未加密):

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

assertRedirect

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

$response->assertRedirect($uri);

assertSee

断言给定字符串包含在响应中:

$response->assertSee($value);

assertSeeInOrder

断言给定字符串按顺序包含在响应中:

$response->assertSeeInOrder(array $values);

assertSeeText

断言给定字符串包含在响应文本中:

$response->assertSeeText($value);

assertSeeTextInOrder

断言给定字符串按顺序包含在给定响应文本中:

$response->assertSeeTextInOrder(array $values);

assertSessionHas

断言会话包含给定数据:

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

assertSessionHasAll

断言会话有给定列表的值:

$response->assertSessionHasAll(array $data);

assertSessionHasErrors

断言会话包含给定字段的错误:

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

assertSessionHasErrorsIn

断言会话有给定错误:

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

assertSessionHasNoErrors

断言会话没有错误:

$response->assertSessionHasNoErrors();

assertSessionMissing

断言会话不包含给定键:

$response->assertSessionMissing($key);

assertStatus

断言响应有给定状态码:

$response->assertStatus($code);

assertSuccessful

断言响应有成功的状态码:

$response->assertSuccessful();

assertViewHas

断言响应视图是给定的数据:

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

assertViewHasAll

断言响应视图有给定列表的数据:

$response->assertViewHasAll(array $data);

assertViewIs

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

$response->assertViewIs($value);

assertViewMissing

断言响应视图不存在给定数据:

$response->assertViewMissing($key);

断言认证

Laravel 也为 PHPUnit 测试提供了各种认证相关的断言:

方法 描述
$this->assertAuthenticated($guard = null); 断言用户已认证
$this->assertGuest($guard = null); 断言用户未认证
$this->assertAuthenticatedAs($user, $guard = null); 断言给定用户已认证
$this->assertCredentials(array $credentials, $guard = null); 断言给定凭证是有效的
$this->assertInvalidCredentials(array $credentials, $guard = null); 断言给定凭证是无效的