# HTTP Tests
- [介绍](#introduction)
- [创建请求](#making-requests)
- [自定义请求头](#customizing-request-headers)
- [Cookies](#cookies)
- [Session / 身份验证](#session-and-authentication)
- [调试响应](#debugging-responses)
- [测试 JSON APIs](#testing-json-apis)
- [测试文件上传](#testing-file-uploads)
- [测试视图](#testing-views)
- [渲染模板 & 组件](#rendering-blade-and-components)
- [可用断言](#available-assertions)
- [响应断言](#response-assertions)
- [身份验证断言](#authentication-assertions)
## 介绍
Laravel 提供了一个非常流畅的 API,用于向您的应用程序发出 HTTP 请求并检查输出。例如,看一下下面定义的功能测试:
get('/');
$response->assertStatus(200);
}
}
例子中 `get` 方法向应用程序发出 `GET` 请求,而 `assertStatus` 方法则断言返回的响应具有给定的 HTTP 状态代码。除了这个简单的断言之外,Laravel 还包含用于检查响应头、内容、JSON 结构等的各种断言。
## 创建请求
要向您的应用程序发出请求,您可以在测试中调用 `get`,`post`,`put`,`patch` 或 `delete` 方法。这些方法实际上不会向您的应用程序发出 「真实的」 HTTP 请求,而是在内部模拟整个网络请求。
测试请求方法不返回 `Illuminate\Http\Response` 实例,而是返回 `Illuminate\Testing\TestResponse` 实例,该实例提供了 [各种有用的断言](#available-assertions),可让您检查应用程序的响应:
get('/');
$response->assertStatus(200);
}
}
通常,您的每个测试应该只向您的应用发出一个请求。如果在单个测试方法中执行多个请求,则可能会出现意外行为。
> 技巧:为了方便起见,运行测试时会自动禁用 CSRF 中间件。
### 自定义请求头
您可以使用此 withHeaders 方法自定义请求的标头,然后再将其发送到应用程序。这使您可以将任何想要的自定义标头添加到请求中:
withHeaders([
'X-Header' => 'Value',
])->post('/user', ['name' => 'Sally']);
$response->assertStatus(201);
}
}
### Cookies
在发送请求前你可以使用`withCookie ` 或 `withCookies` 方法设置 cookie 。`withCookie` 接受 cookie 的名称和值这两个参数,而 `withCookies `方法接受一个名称 / 值对数组:
withCookie('color', 'blue')->get('/');
$response = $this->withCookies([
'color' => 'blue',
'name' => 'Taylor',
])->get('/');
}
}
### Session / Authentication
Laravel 提供了几个可在 HTTP 测试时使用 Session 的辅助函数。首先,你需要传递一个数组给 `withSession` 方法来设置 session 数据。这样在应用程序的测试请求发送之前,就会先去给数据加载 session:
withSession(['banned' => false])->get('/');
}
}
Laravel 的 session 通常用于维护当前已验证用户的状态。因此,`actingAs` 方法提供了一种将给定用户作为当前用户进行身份验证的便捷方法。例如, 我们可以使用 [工厂模式](/docs/laravel/8.x/database-testing#writing-factories) 生成并验证用户:
create();
$response = $this->actingAs($user)
->withSession(['banned' => false])
->get('/');
}
}
你也可以通过传递看守器名称作为 `actingAs` 方法的第二参数以指定用户通过哪种看守器来认证:
$this->actingAs($user, 'api')
### 调试响应
在向您的应用程序发出测试请求之后,可以使用 `dump`、`dumpHeaders` 和 `dumpSession` 方法来检查和调试响应内容:
get('/');
$response->dumpHeaders();
$response->dumpSession();
$response->dump();
}
}
## 测试 JSON APIs
Laravel 也提供了几个辅助函数来测试 JSON APIs 和其响应。例如,`json`、`getJson`、`postJson`、`putJson`、`patchJson`、`deleteJson` 以及 `optionsJson` 可以被用于发送各种 HTTP 动作。你也可以轻松地将数据和请求头传递到这些方法中。首先,让我们实现一个测试示例, 发送 `POST` 请求到 `/api/user`,并断言返回的期望数据:
postJson('/api/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertJson([
'created' => true,
]);
}
}
此外,JSON 响应数据可以作为响应上的数组变量进行访问,从而使您可以方便地检查 JSON 响应中返回的各个值:
$this->assertTrue($response['created']);
> 技巧:`AssertJson` 方法将响应转换为数组,并利用 `PHPUnit::assertArraySubset` 验证给定数组是否存在于应用程序返回的 JSON 响应中。因此,如果 JSON 响应中还有其他属性,则只要存在给定的片段,此测试仍将通过。
#### 验证 JSON 完全匹配
如前所述,`assertJson` 方法可用于断言 JSON 响应中存在 JSON 片段。如果您想验证给定数组是否与应用程序返回的 JSON **完全匹配**,则应使用 `assertExactJson` 方法:
json('POST', '/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertExactJson([
'created' => true,
]);
}
}
#### 验证 JSON 路径
如果你想验证 JSON 响应是否包含指定路径上的某些给定数据,可以使用 `assertJsonPath` 方法:
json('POST', '/user', ['name' => 'Sally']);
$response
->assertStatus(201)
->assertJsonPath('team.owner.name', 'Darian');
}
}
## 测试文件上传
`Illuminate\Http\UploadedFile` 提供了一个 `fake` 方法用于生成虚拟的文件或者图像以供测试之用。它可以和 `Storage` facade 的 `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');
#### 虚拟文件定制
在使用 `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` 的实例,它提供了几个方法来方便地断言视图的内容:
view('welcome', ['name' => 'Taylor']);
$view->assertSee('Taylor');
}
}
`TestView` 对象提供了以下断言方法:`assertSee`、`assertSeeInOrder`、`assertSeeText`、`assertSeeTextInOrder`、`assertDontSee` 和 `assertDontSeeText`。
如果需要,你可以通过将 `TestView` 实例转换为一个字符串获得原始的视图内容:
$contents = (string) $this->view('welcome');
#### 共享错误
一些视图可能依赖于 Laravel 提供的 [全局错误包](/docs/laravel/8.x/validation#quick-displaying-the-validation-errors) 中共享的错误。要在错误包中生成错误消息,可以使用 `withViewErrors` 方法:
$view = $this->withViewErrors([
'name' => ['Please provide a valid name.']
])->view('form');
$view->assertSee('Please provide a valid name.');
### 渲染模板 & 组件
必要的话,你可以使用 `blade` 方法来计算和呈现原始的 [Blade](/docs/laravel/8.x/blade) 字符串。与 `view` 方法一样,`blade` 方法返回的是 `Illuminate\Testing\TestView` 的实例:
$view = $this->blade(
'',
['name' => 'Taylor']
);
$view->assertSee('Taylor');
您可以使用 `component` 方法来评估和渲染 [Blade组件](/docs/laravel/8.x/blade#components)。类似于 `view` 方法,`component` 方法返回一个 `Illuminate\Testing\TestView` 的实例:
$view = $this->component(Profile::class, ['name' => 'Taylor']);
$view->assertSee('Taylor');
## 可用断言
### 响应断言
Laravel的 `Illuminate \ Testing \ TestResponse` 类提供了各种自定义断言方法,您可以在测试应用程序时使用它们。可以在由 `json`、`get`、`post`、`put` 和 `delete` 方法返回的响应上访问这些断言:
[assertCookie](#assert-cookie)
[assertCookieExpired](#assert-cookie-expired)
[assertCookieNotExpired](#assert-cookie-not-expired)
[assertCookieMissing](#assert-cookie-missing)
[assertCreated](#assert-created)
[assertDontSee](#assert-dont-see)
[assertDontSeeText](#assert-dont-see-text)
[assertExactJson](#assert-exact-json)
[assertForbidden](#assert-forbidden)
[assertHeader](#assert-header)
[assertHeaderMissing](#assert-header-missing)
[assertJson](#assert-json)
[assertJsonCount](#assert-json-count)
[assertJsonFragment](#assert-json-fragment)
[assertJsonMissing](#assert-json-missing)
[assertJsonMissingExact](#assert-json-missing-exact)
[assertJsonMissingValidationErrors](#assert-json-missing-validation-errors)
[assertJsonPath](#assert-json-path)
[assertJsonStructure](#assert-json-structure)
[assertJsonValidationErrors](#assert-json-validation-errors)
[assertLocation](#assert-location)
[assertNoContent](#assert-no-content)
[assertNotFound](#assert-not-found)
[assertOk](#assert-ok)
[assertPlainCookie](#assert-plain-cookie)
[assertRedirect](#assert-redirect)
[assertSee](#assert-see)
[assertSeeInOrder](#assert-see-in-order)
[assertSeeText](#assert-see-text)
[assertSeeTextInOrder](#assert-see-text-in-order)
[assertSessionHas](#assert-session-has)
[assertSessionHasInput](#assert-session-has-input)
[assertSessionHasAll](#assert-session-has-all)
[assertSessionHasErrors](#assert-session-has-errors)
[assertSessionHasErrorsIn](#assert-session-has-errors-in)
[assertSessionHasNoErrors](#assert-session-has-no-errors)
[assertSessionDoesntHaveErrors](#assert-session-doesnt-have-errors)
[assertSessionMissing](#assert-session-missing)
[assertStatus](#assert-status)
[assertSuccessful](#assert-successful)
[assertUnauthorized](#assert-unauthorized)
[assertViewHas](#assert-view-has)
[assertViewHasAll](#assert-view-has-all)
[assertViewIs](#assert-view-is)
[assertViewMissing](#assert-view-missing)
#### assertCookie
断言响应中包含给定的 cookie:
$response->assertCookie($cookieName, $value = null);
#### assertCookieExpired
断言响应包含给定的过期的 cookie:
$response->assertCookieExpired($cookieName);
#### assertCookieNotExpired
断言响应包含给定的未过期的 cookie:
$response->assertCookieNotExpired($cookieName);
#### assertCookieMissing
断言响应不包含给定的 cookie:
$response->assertCookieMissing($cookieName);
#### assertCreated
断言做状态代码为 201 的响应:
$response->assertCreated();
#### assertDontSee
断言给定的字符串不包含在响应中。除非传递第二个参数 `false`,否则此断言将给定字符串进行转义后匹配:
$response->assertDontSee($value, $escaped = true);
#### assertDontSeeText
断言给定的字符串不包含在响应文本中。除非您传递第二个参数 `false`,否则该断言将自动转义给定的字符串。该方法将在做出断言之前将响应内容传递给 PHP 的 `strip_tags` 函数:
$response->assertDontSeeText($value, $escaped = true);
#### assertExactJson
断言响应包含与给定 JSON 数据的完全匹配:
$response->assertExactJson(array $data);
#### assertForbidden
断言响应中有禁止访问 (403) 状态码:
$response->assertForbidden();
#### assertHeader
断言给定的 header 在响应中存在:
$response->assertHeader($headerName, $value = null);
#### assertHeaderMissing
断言给定的 header 在响应中不存在:
$response->assertHeaderMissing($headerName);
#### assertJson
断言响应包含给定的 JSON 数据:
$response->assertJson(array $data, $strict = false);
`AssertJson` 方法将响应转换为数组,并利用 `PHPUnit::assertArraySubset` 验证给定数组是否存在于应用程序返回的 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']);
#### assertJsonMissing
断言响应未包含给定的 JSON 片段:
$response->assertJsonMissing(array $data);
#### assertJsonMissingExact
断言响应不包含确切的 JSON 片段:
$response->assertJsonMissingExact(array $data);
#### assertJsonMissingValidationErrors
断言响应响应对于给定的键没有 JSON 验证错误:
Assert that the response has no JSON validation errors for the given keys:
$response->assertJsonMissingValidationErrors($keys);
#### assertJsonPath
断言响应包含指定路径上的给定数据:
$response->assertJsonPath($path, array $data, $strict = true);
例如,如果您的应用程序返回的 JSON 响应包含以下数据:
```js
{
"user": {
"name": "Steve Schoger"
}
}
```
您可以断言 `user` 对象的 `name` 属性匹配给定值,如下所示:
$response->assertJsonPath('user.name', 'Steve Schoger');
#### assertJsonStructure
断言响应具有给定的 JSON 结构:
$response->assertJsonStructure(array $structure);
例如,如果您的应用程序返回的 JSON 响应包含以下数据:
```js
{
"user": {
"name": "Steve Schoger"
}
}
```
您可以断言 JSON 结构符合您的期望,如下所示:
$response->assertJsonStructure([
'user' => [
'name',
]
]);
#### assertJsonValidationErrors
断言响应具有给定键的给定 JSON 验证错误。在对响应进行断言时应使用此方法,在该响应中,验证错误以 JSON 结构形式返回,而不是闪存到 session :
$response->assertJsonValidationErrors(array $data);
#### assertLocation
断言响应在 `Location` 头部中具有给定的 URI 值:
$response->assertLocation($uri);
#### assertNoContent
断言响应具有给定的 HTTP 状态码且没有内容:
$response->assertNoContent($status = 204);
#### assertNotFound
断言响应具有未找到(404)HTTP 状态码:
$response->assertNotFound();
#### assertOk
断言响应有 200 状态码:
$response->assertOk();
#### assertPlainCookie
断言响应包含给定的 cookie (未加密):
$response->assertPlainCookie($cookieName, $value = null);
#### assertRedirect
断言响应会重定向到给定的 URI:
$response->assertRedirect($uri);
#### assertSee
断言给定的字符串包含在响应中。除非传递第二个参数 `false` ,否则此断言将给定字符串进行转义后匹配:
$response->assertSee($value, $escaped = true);
#### assertSeeInOrder
断言给定的字符串按顺序包含在响应中。除非传递第二个参数 `false` ,否则此断言将给定字符串进行转义后匹配:
$response->assertSeeInOrder(array $values, $escaped = true);
#### assertSeeText
断言给定字符串包含在响应文本中。除非传递第二个参数 `false`,否则此断言将给定字符串进行转义后匹配。在做出断言之前,响应内容将被传递到 PHP 的 `strip_tags` 函数:
$response->assertSeeText($value, $escaped = true);
#### assertSeeTextInOrder
断言给定的字符串按顺序包含在响应的文本中。除非传递第二个参数 `false` ,否则此断言将给定字符串进行转义后匹配。在做出断言之前,响应内容将被传递到 PHP 的 `strip_tags` 函数:
$response->assertSeeTextInOrder(array $values, $escaped = true);
#### assertSessionHas
断言 Session 包含给定的数据段:
$response->assertSessionHas($key, $value = null);
#### assertSessionHasInput
session 在 [闪存输入数组](/docs/laravel/8.x/responses#redirecting-with-flashed-session-data) 中断言具有给定值:
$response->assertSessionHasInput($key, $value = null);
#### assertSessionHasAll
断言 Session 中具有给定的键/值对列表:
$response->assertSessionHasAll(array $data);
例如,如果您的应用程序会话包含 `name` 和 `status` 键,则可以断言它们存在并且具有指定的值,如下所示:
$response->assertSessionHasAll([
'name' => 'Taylor Otwell',
'status' => 'active',
]);
#### assertSessionHasErrors
断言 session 包含给定 `$keys` 的 Laravel 验证错误。如果 `$keys` 是关联数组,则断言 session 包含每个字段(key)的特定错误消息(value)。测试将闪存验证错误到 session 的路由时,应使用此方法,而不是将其作为 JSON 结构返回:
$response->assertSessionHasErrors(
array $keys, $format = null, $errorBag = 'default'
);
例如,要断言 `name` 和 `email` 字段具有已闪现到 session 的验证错误消息,可以调用`assertSessionHasErrors` 方法,如下所示:
$response->assertSessionHasErrors(['name', 'email']);
或者,您可以断言给定字段具有特定的验证错误消息:
$response->assertSessionHasErrors([
'name' => 'The given name was invalid.'
]);
#### assertSessionHasErrorsIn
断言会话在特定的 [错误包](/docs/laravel/8.x/validation#named-error-bags) 中包含给定 `$keys` 的错误。如果 `$keys` 是一个关联数组,则断言该 session 在错误包内包含每个字段(键)的特定错误消息(值):
$response->assertSessionHasErrorsIn($errorBag, $keys = [], $format = null);
#### assertSessionHasNoErrors
断言 session 没有验证错误:
$response->assertSessionHasNoErrors();
#### assertSessionDoesntHaveErrors
断言 session 没有验证错误:
Assert that the session has no validation errors for the given keys:
$response->assertSessionDoesntHaveErrors($keys = [], $format = null, $errorBag = 'default');
#### assertSessionMissing
断言 session 中缺少指定的 $key:
$response->assertSessionMissing($key);
#### assertStatus
断言响应指定的 http 状态码:
$response->assertStatus($code);
#### assertSuccessful
断言响应一个成功的状态码 (>= 200 且 < 300) :
$response->assertSuccessful();
#### assertUnauthorized
断言一个未认证的状态码 (401):
$response->assertUnauthorized();
#### assertViewHas
断言为响应视图提供了一个键值对数据:
$response->assertViewHas($key, $value = null);
另外,视图数据可以作为响应上的数组变量访问,从而使您可以方便地检查它:
$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);