HTTP 測試

簡介

Laravel 提供了一個語義化的 API,這個 API 可以建立連到我們專案的 HTTP Request(請求),並讓我們能加以檢查 Response(回覆)。舉例來說,我們來看看下面定義的這個 Feature Test:

1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Tests\TestCase;
8 
9class ExampleTest extends TestCase
10{
11 /**
12 * A basic test example.
13 *
14 * @return void
15 */
16 public function test_a_basic_request()
17 {
18 $response = $this->get('/');
19 
20 $response->assertStatus(200);
21 }
22}
1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Tests\TestCase;
8 
9class ExampleTest extends TestCase
10{
11 /**
12 * A basic test example.
13 *
14 * @return void
15 */
16 public function test_a_basic_request()
17 {
18 $response = $this->get('/');
19 
20 $response->assertStatus(200);
21 }
22}

get 方法會建立連到專案的 GET Request,而 assertStatus 方法則會判斷回傳的 Response 是否為給定的 HTTP 狀態碼。出了這種簡單的 Assertion(判斷提示) 外,Laravel 也提供了各種不同的 Assertion,可用來檢查 Response 的 Header(標頭)、內容、JSON 結構…等。

建立 Request

若要建立連到專案的 Request,可以在測試中叫用 getpostputpatchdelete 方法。這些方法不會真的建立「真正的」HTTP Request,而是在程式內部模擬一段網路連線。

這些測試 Request 方法不是回傳 Illuminate\Http\Response 實體,而是回傳 Illuminate\Testing\TestResponse 的實體。TestResponse 實體提供了各種實用的 Assertion,可讓我們檢查專案的 Response:

1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Tests\TestCase;
8 
9class ExampleTest extends TestCase
10{
11 /**
12 * A basic test example.
13 *
14 * @return void
15 */
16 public function test_a_basic_request()
17 {
18 $response = $this->get('/');
19 
20 $response->assertStatus(200);
21 }
22}
1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Tests\TestCase;
8 
9class ExampleTest extends TestCase
10{
11 /**
12 * A basic test example.
13 *
14 * @return void
15 */
16 public function test_a_basic_request()
17 {
18 $response = $this->get('/');
19 
20 $response->assertStatus(200);
21 }
22}

一般來說,每個測試都應只向專案建立一個 Request。若在單一測試方法內建立多個 Request,可能會發生未預期的行為。

lightbulb

為了方便起見,在執行測試時會自動禁用 CSRF Middleware。

自訂 Request 的 Header

可使用 withHeaders 方法來在 Request 傳送到專案前先自訂 Request 的 Header。使用這個方法,我們就可以自行加上任何需要的自定 Request:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic functional test example.
11 *
12 * @return void
13 */
14 public function test_interacting_with_headers()
15 {
16 $response = $this->withHeaders([
17 'X-Header' => 'Value',
18 ])->post('/user', ['name' => 'Sally']);
19 
20 $response->assertStatus(201);
21 }
22}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic functional test example.
11 *
12 * @return void
13 */
14 public function test_interacting_with_headers()
15 {
16 $response = $this->withHeaders([
17 'X-Header' => 'Value',
18 ])->post('/user', ['name' => 'Sally']);
19 
20 $response->assertStatus(201);
21 }
22}

Cookie

我們可以使用 withCookiewithCookies 方法來在建立 Request 前設定 Cookie 值。withCookie 方法有兩個引數:Cookie 名稱與 Cookie 值。withCookies 方法則接受一組名稱/值配對的陣列:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 public function test_interacting_with_cookies()
10 {
11 $response = $this->withCookie('color', 'blue')->get('/');
12 
13 $response = $this->withCookies([
14 'color' => 'blue',
15 'name' => 'Taylor',
16 ])->get('/');
17 }
18}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 public function test_interacting_with_cookies()
10 {
11 $response = $this->withCookie('color', 'blue')->get('/');
12 
13 $response = $this->withCookies([
14 'color' => 'blue',
15 'name' => 'Taylor',
16 ])->get('/');
17 }
18}

Session 與身份驗證

Laravel 提供了各種在 HTTP 測試期間處理 Session 的輔助函式。首先,我們可以使用給定 withSession 方法來以給定的陣列設定 Session 資料。若要在向專案傳送 Request 前先在 Session 內載入資料,就適合使用這個方法:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 public function test_interacting_with_the_session()
10 {
11 $response = $this->withSession(['banned' => false])->get('/');
12 }
13}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 public function test_interacting_with_the_session()
10 {
11 $response = $this->withSession(['banned' => false])->get('/');
12 }
13}

由於 Laravel 的 Session 通常是用來保存目前登入使用者的狀態,因此,也有一個 actingAs 輔助函式方法,可更簡單地讓我們將給定的使用者登入為目前使用者。舉例來說,我們可以使用 Model Factory 來產生並登入使用者:

1<?php
2 
3namespace Tests\Feature;
4 
5use App\Models\User;
6use Tests\TestCase;
7 
8class ExampleTest extends TestCase
9{
10 public function test_an_action_that_requires_authentication()
11 {
12 $user = User::factory()->create();
13 
14 $response = $this->actingAs($user)
15 ->withSession(['banned' => false])
16 ->get('/');
17 }
18}
1<?php
2 
3namespace Tests\Feature;
4 
5use App\Models\User;
6use Tests\TestCase;
7 
8class ExampleTest extends TestCase
9{
10 public function test_an_action_that_requires_authentication()
11 {
12 $user = User::factory()->create();
13 
14 $response = $this->actingAs($user)
15 ->withSession(['banned' => false])
16 ->get('/');
17 }
18}

我們可以使用 actingAs 方法的第二個引數來指定要使用哪個 Guard 來驗證給定使用者:

1$this->actingAs($user, 'web')
1$this->actingAs($user, 'web')

為 Response 進行偵錯

向專案建立測試 Request 後,可使用 dumpdumpHeadersdumpSession 方法來取得 Response 的內容或對其偵錯:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic test example.
11 *
12 * @return void
13 */
14 public function test_basic_test()
15 {
16 $response = $this->get('/');
17 
18 $response->dumpHeaders();
19 
20 $response->dumpSession();
21 
22 $response->dump();
23 }
24}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic test example.
11 *
12 * @return void
13 */
14 public function test_basic_test()
15 {
16 $response = $this->get('/');
17 
18 $response->dumpHeaders();
19 
20 $response->dumpSession();
21 
22 $response->dump();
23 }
24}

或者,我們可以使用 ddddHeadersddSession 方法來將該 Response 相關的資料傾印出來,並停止執行:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic test example.
11 *
12 * @return void
13 */
14 public function test_basic_test()
15 {
16 $response = $this->get('/');
17 
18 $response->ddHeaders();
19 
20 $response->ddSession();
21 
22 $response->dd();
23 }
24}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic test example.
11 *
12 * @return void
13 */
14 public function test_basic_test()
15 {
16 $response = $this->get('/');
17 
18 $response->ddHeaders();
19 
20 $response->ddSession();
21 
22 $response->dd();
23 }
24}

處理 Exception

有時候,我們可能會想測試專案是否有擲回特定的 Exception(例外)。為了避免該 Exception 被 Laravel 的 Exception Handler(處理常式)攔截並轉為 HTTP Response,請在建立 Request 前先叫用 withoutExceptionHandling 方法:

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

此外,若想確保專案中未使用到 PHP 或其他專案使用套件中宣告 Deprecated(已過時) 的功能,我們可以在建立 Request 前叫用 withoutDeprecationHandling 方法。停用 Deprecation Handling 後,Deprecation Warning(警告) 會被轉換為 Exception,並導致測試執行失敗:

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

測試 JSON API

Laravel 中也提供了數種可用來測試 JSON API 與起 Response 的輔助函式。舉例來說,jsongetJsonpostJsonputJsonpatchJsondeleteJsonoptionsJson 等方法可用來以各種 HTTP Verb(指令動詞) 來建立 JSON Request。我們也可以輕鬆地將資料與 Header 傳給這些方法。若要開始測試 JSON API,我們先來撰寫一個建立連到 /api/userPOST Request,並撰寫預期回傳 JSON 資料的 Assertion:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic functional test example.
11 *
12 * @return void
13 */
14 public function test_making_an_api_request()
15 {
16 $response = $this->postJson('/api/user', ['name' => 'Sally']);
17 
18 $response
19 ->assertStatus(201)
20 ->assertJson([
21 'created' => true,
22 ]);
23 }
24}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic functional test example.
11 *
12 * @return void
13 */
14 public function test_making_an_api_request()
15 {
16 $response = $this->postJson('/api/user', ['name' => 'Sally']);
17 
18 $response
19 ->assertStatus(201)
20 ->assertJson([
21 'created' => true,
22 ]);
23 }
24}

此外,在 Response 上,我們可以用陣列變數的形式來存取 JSON Response 的資料。這麼一來我們就能方便地檢查 JSON Response 中回傳的各個值:

1$this->assertTrue($response['created']);
1$this->assertTrue($response['created']);
lightbulb

assertJson 方法會將 Response 轉換為陣列,並使用 PHPUnit::assertArraySubset 來驗證給定陣列是否有包含在專案回傳的 JSON Response 中。所以,如果在 JSON Response 中有包含其他屬性,只要給定的部分有包含在 JSON 裡,測試就會通過。

判斷 JSON 是否完全符合

剛才也提到過,assertJson 方法可用來判斷給定的部分 JSON 是否有包含在 JSON Response 中。若想檢查給定的陣列是否與專案回傳的 JSON 完全符合,請使用 assertExactJson 方法:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic functional test example.
11 *
12 * @return void
13 */
14 public function test_asserting_an_exact_json_match()
15 {
16 $response = $this->postJson('/user', ['name' => 'Sally']);
17 
18 $response
19 ->assertStatus(201)
20 ->assertExactJson([
21 'created' => true,
22 ]);
23 }
24}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic functional test example.
11 *
12 * @return void
13 */
14 public function test_asserting_an_exact_json_match()
15 {
16 $response = $this->postJson('/user', ['name' => 'Sally']);
17 
18 $response
19 ->assertStatus(201)
20 ->assertExactJson([
21 'created' => true,
22 ]);
23 }
24}

判斷 JSON 路徑

若想檢查 JSON Response 中,特定路徑上是否有包含給定資料,可使用 assertJsonPath 方法:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic functional test example.
11 *
12 * @return void
13 */
14 public function test_asserting_a_json_paths_value()
15 {
16 $response = $this->postJson('/user', ['name' => 'Sally']);
17 
18 $response
19 ->assertStatus(201)
20 ->assertJsonPath('team.owner.name', 'Darian');
21 }
22}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 /**
10 * A basic functional test example.
11 *
12 * @return void
13 */
14 public function test_asserting_a_json_paths_value()
15 {
16 $response = $this->postJson('/user', ['name' => 'Sally']);
17 
18 $response
19 ->assertStatus(201)
20 ->assertJsonPath('team.owner.name', 'Darian');
21 }
22}

Fluent JSON 測試

Laravel 也提供了另一種較好看、語義化的方法來讓我們測試專案的 JSON Response。若要用這種方法來測試 JSON Response,只需要傳入一個閉包給 assertJson。在叫用這個閉包時,該閉包會收到一個 Illuminate\Testing\Fluent\AssertableJson 實體。AssertableJson 可用來對專案回傳的 JSON 做 Assertion。where 方法可用來對 JSON 中特定屬性做 Assertion。而 missing 方法可用來判斷 JSON 中是否不含特定屬性:

1use Illuminate\Testing\Fluent\AssertableJson;
2 
3/**
4 * A basic functional test example.
5 *
6 * @return void
7 */
8public function test_fluent_json()
9{
10 $response = $this->getJson('/users/1');
11 
12 $response
13 ->assertJson(fn (AssertableJson $json) =>
14 $json->where('id', 1)
15 ->where('name', 'Victoria Faith')
16 ->missing('password')
17 ->etc()
18 );
19}
1use Illuminate\Testing\Fluent\AssertableJson;
2 
3/**
4 * A basic functional test example.
5 *
6 * @return void
7 */
8public function test_fluent_json()
9{
10 $response = $this->getJson('/users/1');
11 
12 $response
13 ->assertJson(fn (AssertableJson $json) =>
14 $json->where('id', 1)
15 ->where('name', 'Victoria Faith')
16 ->missing('password')
17 ->etc()
18 );
19}

瞭解 etc 方法

在上述的範例中,讀者可能有注意到我們在 Assersion 串列的最後面叫用了 etc 方法。叫用該方法可讓 Laravel 知道在 JSON 物件中可能還有其他屬性。在沒有使用 etc 方法的情況下,若 JSON 物件中還有其他屬性存在,而我們未對這些屬性進行 Assersion 時,測試會執行失敗。

這種在沒有呼叫 etc 方法的情況下會使測試失敗的行為,是為了避免讓我們在 JSON Response 中不小心暴露出機敏資訊,所以才強制我們要針對所有屬性做 Assersion,或是使用 etc 方法來顯式允許其他額外的屬性。

判斷屬性存在/不存在

若要判斷某個屬性存在或不存在,可使用 hasmissing 方法:

1$response->assertJson(fn (AssertableJson $json) =>
2 $json->has('data')
3 ->missing('message')
4);
1$response->assertJson(fn (AssertableJson $json) =>
2 $json->has('data')
3 ->missing('message')
4);

此外,使用 hasAllmissingAll 方法,就可以同時針對多個屬性判斷存在或不存在:

1$response->assertJson(fn (AssertableJson $json) =>
2 $json->hasAll('status', 'data')
3 ->missingAll('message', 'code')
4);
1$response->assertJson(fn (AssertableJson $json) =>
2 $json->hasAll('status', 'data')
3 ->missingAll('message', 'code')
4);

我們可以使用 hasAny 方法來判斷給定屬性列表中是否至少有一個屬性存在:

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

判斷 JSON Collection

通常來說,在 Route 中回傳的 Json Response 會包含多個項目,如多位使用者:

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

在這些情況下,我們可以使用 Fluent JSON 物件的 has 方法來針對該 Response 中的使用者進行 Assertion。舉例來說,我們來判斷 JSON Response 中是否有包含三位使用者。接著,我們再使用 first 方法來對該 Collection 中的使用者做 Assertion。first 方法接受一個閉包,該閉包會收到另一個可 Assert 的 JSON 字串,我們可以使用這個 JSON 字串來針對該 JSON Collection 中的第一個物件進行 Assertion:

1$response
2 ->assertJson(fn (AssertableJson $json) =>
3 $json->has(3)
4 ->first(fn ($json) =>
5 $json->where('id', 1)
6 ->where('name', 'Victoria Faith')
7 ->missing('password')
8 ->etc()
9 )
10 );
1$response
2 ->assertJson(fn (AssertableJson $json) =>
3 $json->has(3)
4 ->first(fn ($json) =>
5 $json->where('id', 1)
6 ->where('name', 'Victoria Faith')
7 ->missing('password')
8 ->etc()
9 )
10 );

限定範圍的 JSON Collection Assertion

有時候,Route 可能會回傳被指派為命名索引鍵的 JSON Collection:

1Route::get('/users', function () {
2 return [
3 'meta' => [...],
4 'users' => User::all(),
5 ];
6})
1Route::get('/users', function () {
2 return [
3 'meta' => [...],
4 'users' => User::all(),
5 ];
6})

在測試這類 Route 時,可以使用 has 方法來判斷該 Collection 中的項目數。此外,也可以使用 has 方法來在一連串的 Assertion 間限制判斷的範圍:

1$response
2 ->assertJson(fn (AssertableJson $json) =>
3 $json->has('meta')
4 ->has('users', 3)
5 ->has('users.0', fn ($json) =>
6 $json->where('id', 1)
7 ->where('name', 'Victoria Faith')
8 ->missing('password')
9 ->etc()
10 )
11 );
1$response
2 ->assertJson(fn (AssertableJson $json) =>
3 $json->has('meta')
4 ->has('users', 3)
5 ->has('users.0', fn ($json) =>
6 $json->where('id', 1)
7 ->where('name', 'Victoria Faith')
8 ->missing('password')
9 ->etc()
10 )
11 );

不過,除了一次對 users Collection 呼叫兩次 has 方法以外,我們也可以只呼叫一次,並提供一個閉包作為該方法的第三個引數。傳入閉包時,Laravel 會自動叫用該閉包,並將作用範圍限定在該 Collection 的第一個項目:

1$response
2 ->assertJson(fn (AssertableJson $json) =>
3 $json->has('meta')
4 ->has('users', 3, fn ($json) =>
5 $json->where('id', 1)
6 ->where('name', 'Victoria Faith')
7 ->missing('password')
8 ->etc()
9 )
10 );
1$response
2 ->assertJson(fn (AssertableJson $json) =>
3 $json->has('meta')
4 ->has('users', 3, fn ($json) =>
5 $json->where('id', 1)
6 ->where('name', 'Victoria Faith')
7 ->missing('password')
8 ->etc()
9 )
10 );

判斷 JSON 型別

我們可能會想檢查 JSON Response 中的某些屬性是否為特定的型別。Illuminate\Testing\Fluent\AssertableJson 類別中,提供了 whereTypewhereAllType 方法可讓我們檢查 JSON 屬性中的型別:

1$response->assertJson(fn (AssertableJson $json) =>
2 $json->whereType('id', 'integer')
3 ->whereAllType([
4 'users.0.name' => 'string',
5 'meta' => 'array'
6 ])
7);
1$response->assertJson(fn (AssertableJson $json) =>
2 $json->whereType('id', 'integer')
3 ->whereAllType([
4 'users.0.name' => 'string',
5 'meta' => 'array'
6 ])
7);

我們也可以使用 | 字元來指定多個型別,或者,也可以傳入一組型別陣列作為 whereType 的第二個引數。若 Response 值符合任意列出的型別,則該 Assertion 會執行成功:

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

whereTypewhereAllType 方法可支援下列型別: stringintegerdoublebooleanarraynull

測試檔案上傳

Illuminate\Http\UploadedFile 類別提供了一個 fake 方法,可用來產生用於測試的假檔案或圖片。只要將 UploadedFilefake 方法與 Storage Facade 的 fake 方法一起使用,我們就能大幅簡化測試檔案上傳的過程。舉例來說,我們可以將這兩個功能搭配使用,來測試某個上傳使用者大頭照的表單:

1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Illuminate\Http\UploadedFile;
8use Illuminate\Support\Facades\Storage;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_avatars_can_be_uploaded()
14 {
15 Storage::fake('avatars');
16 
17 $file = UploadedFile::fake()->image('avatar.jpg');
18 
19 $response = $this->post('/avatar', [
20 'avatar' => $file,
21 ]);
22 
23 Storage::disk('avatars')->assertExists($file->hashName());
24 }
25}
1<?php
2 
3namespace Tests\Feature;
4 
5use Illuminate\Foundation\Testing\RefreshDatabase;
6use Illuminate\Foundation\Testing\WithoutMiddleware;
7use Illuminate\Http\UploadedFile;
8use Illuminate\Support\Facades\Storage;
9use Tests\TestCase;
10 
11class ExampleTest extends TestCase
12{
13 public function test_avatars_can_be_uploaded()
14 {
15 Storage::fake('avatars');
16 
17 $file = UploadedFile::fake()->image('avatar.jpg');
18 
19 $response = $this->post('/avatar', [
20 'avatar' => $file,
21 ]);
22 
23 Storage::disk('avatars')->assertExists($file->hashName());
24 }
25}

若想檢查給定的檔案是否不存在,可使用 Storage Facade 的 assertMissing 方法:

1Storage::fake('avatars');
2 
3// ...
4 
5Storage::disk('avatars')->assertMissing('missing.jpg');
1Storage::fake('avatars');
2 
3// ...
4 
5Storage::disk('avatars')->assertMissing('missing.jpg');

自訂 Fake 檔案

在使用 UploadedFile 類別的 fake 方法來建立檔案時,我們可以指定圖片的長寬與檔案大小 (單位為 kB),以更好地測試程式中的表單驗證規則:

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

除了建立圖片外,還可以使用 create 方法來建立任何其他類型的檔案:

1// $sizeInKilobytes = 單位為 kB 的檔案大小
2UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);
1// $sizeInKilobytes = 單位為 kB 的檔案大小
2UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);

若有需要,可傳入 $mimeType 引數,以明顯定義 File 要回傳的 MIME 型別:

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

測試 View

在 Laravel 中,我們也可以在不模擬 HTTP Request 的情況轉譯 View。若要在不模擬 HTTP Request 的情況下轉譯 View,我們可以在測試中呼叫 view 方法。view 方法的參數為 View 的名稱,以及一組可選的資料陣列。該方法會回傳 Illuminate\Testing\TestView 的實體,使用 TestView,我們就能方便地針對 View 的內容進行 Assertion:

1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 public function test_a_welcome_view_can_be_rendered()
10 {
11 $view = $this->view('welcome', ['name' => 'Taylor']);
12 
13 $view->assertSee('Taylor');
14 }
15}
1<?php
2 
3namespace Tests\Feature;
4 
5use Tests\TestCase;
6 
7class ExampleTest extends TestCase
8{
9 public function test_a_welcome_view_can_be_rendered()
10 {
11 $view = $this->view('welcome', ['name' => 'Taylor']);
12 
13 $view->assertSee('Taylor');
14 }
15}

TestView 類別還提供了下列 Assertion 方法:assertSeeassertSeeInOrderassertSeeTextassertSeeTextInOrderassertDontSeeassertDontSeeText

若有需要,我們可以將 TestView 實體轉換型別為字串來取得原始的 View 內容轉譯結果:

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

共用錯誤訊息

有的 View 會使用到 Laravel 所提供的全域 Error Bag中共享的錯誤。若要在全域 Error Bag 中填充錯誤訊息,可使用 withViewErrors 方法:

1$view = $this->withViewErrors([
2 'name' => ['Please provide a valid name.']
3])->view('form');
4 
5$view->assertSee('Please provide a valid name.');
1$view = $this->withViewErrors([
2 'name' => ['Please provide a valid name.']
3])->view('form');
4 
5$view->assertSee('Please provide a valid name.');

轉譯 Blade 與元件

若有需要,可以使用 blade 方法來取值並轉譯原始的 Blade 字串。與 view 方法類似,blade 方法也會回傳 Illuminate\Testing\TestView 的實體:

1$view = $this->blade(
2 '<x-component :name="$name" />',
3 ['name' => 'Taylor']
4);
5 
6$view->assertSee('Taylor');
1$view = $this->blade(
2 '<x-component :name="$name" />',
3 ['name' => 'Taylor']
4);
5 
6$view->assertSee('Taylor');

可以使用 component 方法來取值並轉譯 Blade 元件。與 view 方法類似,component 元件也會回傳 Illuminate\Testing\TestView 的實體:

1$view = $this->component(Profile::class, ['name' => 'Taylor']);
2 
3$view->assertSee('Taylor');
1$view = $this->component(Profile::class, ['name' => 'Taylor']);
2 
3$view->assertSee('Taylor');

可用的 Assertion

Response 的 Assertion

Laravel 的 Illuminate\Testing\TestResponse 類別提供了各種自訂 Assertion 方法供我們在測試程式時使用。這些 Assertion 可在 jsongetpostputdelete 測試方法回傳的 Response 上存取:

判斷 Response 包含給定 Cookie:

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

判斷 Response 包含給定 Cookie,且該 Cookie 已逾期:

1$response->assertCookieExpired($cookieName);
1$response->assertCookieExpired($cookieName);

判斷 Response 包含給定 Cookie,且該 Cookie 未逾期:

1$response->assertCookieNotExpired($cookieName);
1$response->assertCookieNotExpired($cookieName);

判斷 Response 不包含給定 Cookie:

1$response->assertCookieMissing($cookieName);
1$response->assertCookieMissing($cookieName);

assertCreated

判斷 Response 是否為 201 HTTP 狀態碼:

1$response->assertCreated();
1$response->assertCreated();

assertDontSee

判斷程式回傳的 Response 中是否不包含給定字串。除非將第二個引數設為 false,否則該 Assertion 會自動逸出給定的字串:

1$response->assertDontSee($value, $escaped = true);
1$response->assertDontSee($value, $escaped = true);

assertDontSeeText

判斷 Response 的文字中是否不包含給定字串。除非將第二個引數設為 false,否則該 Assertion 會自動逸出給定的字串。該方法會將 Response 的內容傳給 strip_tags PHP 函式,然後再進行判斷:

1$response->assertDontSeeText($value, $escaped = true);
1$response->assertDontSeeText($value, $escaped = true);

assertDownload

判斷 Response 是否為「檔案下載」。一般來說,就表示所叫用的 Route 回傳了 Response::download Response、BinaryFileResponse、 或是 Storage::download Response:

1$response->assertDownload();
1$response->assertDownload();

若有需要,可判斷該下載檔案是否為給定的檔名:

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

assertExactJson

判斷 Response 是否包含與給定的 JSON 資料完全相符的內容:

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

assertForbidden

判斷 Response 是否為 Forbidden(禁止存取) (403) HTTP 狀態碼:

1$response->assertForbidden();
1$response->assertForbidden();

assertHeader

判斷 Response 中是否有給定的 Header 與值:

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

assertHeaderMissing

判斷 Response 中是否不含給定的 Header:

1$response->assertHeaderMissing($headerName);
1$response->assertHeaderMissing($headerName);

assertJson

判斷 Response 是否包含給定的 JSON 資料:

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

assertJson 方法會將 Response 轉換為陣列,並使用 PHPUnit::assertArraySubset 來驗證給定陣列是否有包含在專案回傳的 JSON Response 中。所以,如果在 JSON Response 中有包含其他屬性,只要給定的部分有包含在 JSON 裡,測試就會通過。

assertJsonCount

判斷 Response JSON 是否為一組陣列,且該陣列在給定的索引鍵上有包含預期數量的項目:

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

assertJsonFragment

判斷 Response 中,是否有在任意位置上包含給定的 JSON 資料:

1Route::get('/users', function () {
2 return [
3 'users' => [
4 [
5 'name' => 'Taylor Otwell',
6 ],
7 ],
8 ];
9});
10 
11$response->assertJsonFragment(['name' => 'Taylor Otwell']);
1Route::get('/users', function () {
2 return [
3 'users' => [
4 [
5 'name' => 'Taylor Otwell',
6 ],
7 ],
8 ];
9});
10 
11$response->assertJsonFragment(['name' => 'Taylor Otwell']);

assertJsonMissing

判斷 Response 中是否不包含給定的 JSON 資料:

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

assertJsonMissingExact

判斷 Response 是否不包含完全相符的 JSON 資料:

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

assertJsonMissingValidationErrors

判斷 Response 中,給定的索引鍵上是否不含 JSON 驗證錯誤:

1$response->assertJsonMissingValidationErrors($keys);
1$response->assertJsonMissingValidationErrors($keys);
lightbulb

還有一個更泛用的 assertValid 方法,可用來檢查 Response 是否不含以 JSON 格式回傳的驗證錯誤,並檢查 Session Storage 上是否未有快閃存入錯誤訊息。

assertJsonPath

判斷 Response 中在特定路徑上是否包含給定的資料:

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

舉例來說,若程式回傳了包含下列資料的 JSON Response:

1{
2 "user": {
3 "name": "Steve Schoger"
4 }
5}
1{
2 "user": {
3 "name": "Steve Schoger"
4 }
5}

則我們可以像這樣判斷 user 物件的 name 屬性是否符合給定的值:

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

assertJsonStructure

判斷 Response 是否含有給定的 JSON 結構:

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

舉例來說,若程式回傳了包含下列資料的 JSON Response:

1{
2 "user": {
3 "name": "Steve Schoger"
4 }
5}
1{
2 "user": {
3 "name": "Steve Schoger"
4 }
5}

也可以像這樣檢查 JSON 結構是否符合預期:

1$response->assertJsonStructure([
2 'user' => [
3 'name',
4 ]
5]);
1$response->assertJsonStructure([
2 'user' => [
3 'name',
4 ]
5]);

有時候,程式會回傳的 JSON Response 會包含一組由物件組成的陣列:

1{
2 "user": [
3 {
4 "name": "Steve Schoger",
5 "age": 55,
6 "location": "Earth"
7 },
8 {
9 "name": "Mary Schoger",
10 "age": 60,
11 "location": "Earth"
12 }
13 ]
14}
1{
2 "user": [
3 {
4 "name": "Steve Schoger",
5 "age": 55,
6 "location": "Earth"
7 },
8 {
9 "name": "Mary Schoger",
10 "age": 60,
11 "location": "Earth"
12 }
13 ]
14}

這時,我們可以使用 * 字元來對該陣列中的所有物件進行結構檢查:

1$response->assertJsonStructure([
2 'user' => [
3 '*' => [
4 'name',
5 'age',
6 'location'
7 ]
8 ]
9]);
1$response->assertJsonStructure([
2 'user' => [
3 '*' => [
4 'name',
5 'age',
6 'location'
7 ]
8 ]
9]);

assertJsonValidationErrors

判斷 Response 中,給定的索引鍵上是否有給定的 JSON 驗證錯誤。該方法應用於檢查以 JSON 格式回傳的表單驗證錯誤 Response,而不應用於檢查快閃存入 Session 中的表單驗證錯誤:

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

還有一個更泛用的 assertInvalid 方法,可用來檢查 Response 是否包含以 JSON 格式回傳的驗證錯誤,或是 Session Storage 上是否有快閃存入錯誤訊息。

assertJsonValidationErrorFor

判斷 Response 中,給定的索引鍵上是否有任何的 JSON 驗證規則:

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

assertLocation

判斷 Response 中,Location Header 上是否有給定的 URI 值:

1$response->assertLocation($uri);
1$response->assertLocation($uri);

assertNoContent

判斷 Response 是否為給定的 HTTP 狀態碼,且不含內容:

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

assertNotFound

判斷 Response 是否為 Not Found(找不到) (404) HTTP 狀態碼:

1$response->assertNotFound();
1$response->assertNotFound();

assertOk

判斷 Response 是否為 200 HTTP 狀態碼:

1$response->assertOk();
1$response->assertOk();

判斷 Response 是否包含給定未加密的 Cookie:

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

assertRedirect

判斷 Response 是否為指向給定 URI 的重新導向:

1$response->assertRedirect($uri);
1$response->assertRedirect($uri);

assertRedirectContains

判斷 Response 是否在重新導向至包含給定字串的 URI:

1$response->assertRedirectContains($string);
1$response->assertRedirectContains($string);

assertRedirectToSignedRoute

判斷 Response 是否為指向給定簽名 Route 的重新導向:

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

assertSee

判斷 Response 中是否包含給定字串。除非將第二個引數設為 false,否則該 Assertion 會自動逸出給定的字串:

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

assertSeeInOrder

判斷 Response 中,是否有依照順序包含給定字串。除非將第二個引數設為 false,否則該 Assertion 會自動逸出給定的字串:

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

assertSeeText

判斷 Response 的文字中是否包含給定字串。除非將第二個引數設為 false,否則該 Assertion 會自動逸出給定的字串。該方法會將 Response 的內容傳給 strip_tags PHP 函式,然後再進行判斷:

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

assertSeeTextInOrder

判斷 Response 的文字中,是否有依照順序出現給定的字串。除非將第二個引數設為 false,否則該 Assertion 會自動逸出給定的字串。該方法會將 Response 的內容傳給 strip_tags PHP 函式,然後再進行判斷:

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

assertSessionHas

判斷 Session 是否包含給定的資料:

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

若有需要,assertSessionHas 方法的第二個引數也可以傳入閉包。若閉包回傳 true,則會視為 Assertion 通過:

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

assertSessionHasInput

判斷 Session 中快閃的輸入陣列內是否包含給定值:

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

若有需要若有需要,assertSessionHasInput 方法的第二個引數也可以傳入閉包。若閉包回傳 true,則會視為 Assertion 通過:

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

assertSessionHasAll

判斷 Session 內是否包含給定的索引鍵/值配對陣列:

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

舉例來說,若網站的 Session 內有 namestatus 索引鍵,則可以像這樣來判斷這兩個欄位是否都存在且為特定的值:

1$response->assertSessionHasAll([
2 'name' => 'Taylor Otwell',
3 'status' => 'active',
4]);
1$response->assertSessionHasAll([
2 'name' => 'Taylor Otwell',
3 'status' => 'active',
4]);

assertSessionHasErrors

判斷 Session 內給定的 $keys 中是否有錯誤。若``$keys` 為關聯式陣列,則會判斷 Session 中,各個欄位 (陣列索引鍵) 是否有特定的錯誤訊息 (陣列值)。請勿使用該方法來測試會回傳 JSON 結構的 Route,請使用該方法來測試將驗證錯誤訊息快閃存入 Session 的 Route:

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

舉例來說,若要判斷 nameemail 欄位中是否有快閃存入 Session 的驗證錯誤訊息,可像這樣叫用 assertSessionHasErrors 方法:

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

或者,也可以判斷給定欄位是否有特定的驗證錯誤訊息:

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

assertSessionHasErrorsIn

判斷 Session 中,在特定的 Error Bag 中,給定的 $keys 內是否有錯誤訊息。若 $keys 為關聯式陣列,則會判斷 Session 中各個欄位 (陣列索引鍵) 是否有特定的錯誤訊息 (陣列值):

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

assertSessionHasNoErrors

判斷 Session 中是否無驗證錯誤:

1$response->assertSessionHasNoErrors();
1$response->assertSessionHasNoErrors();

assertSessionDoesntHaveErrors

判斷 Session 中給定的索引鍵是否無驗證錯誤:

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

assertSessionMissing

判斷 Session 中是否不包含給定的索引鍵:

1$response->assertSessionMissing($key);
1$response->assertSessionMissing($key);

assertStatus

判斷 Response 是否回傳給定的 HTTP 狀態碼:

1$response->assertStatus($code);
1$response->assertStatus($code);

assertSuccessful

判斷 Response 是否有成功的 HTTP 狀態碼 (>= 200 且 < 300):

1$response->assertSuccessful();
1$response->assertSuccessful();

assertUnauthorized

判斷 Response 是否為禁止存取 (401) HTTP 狀態碼:

1$response->assertUnauthorized();
1$response->assertUnauthorized();

assertUnprocessable

判斷 Response 是否為無法處理 (422) HTTP 狀態碼:

1$response->assertUnprocessable();
1$response->assertUnprocessable();

assertValid

判斷 Response 中,不包含給定索引鍵的驗證錯誤。該方法可用來判斷以 JSON 結構回傳驗證錯誤,或是將驗證錯誤快閃存入 Session 的 Response:

1// 判斷無驗證錯誤訊息...
2$response->assertValid();
3 
4// 判斷給定的索引鍵中沒有給定的驗證錯誤訊息...
5$response->assertValid(['name', 'email']);
1// 判斷無驗證錯誤訊息...
2$response->assertValid();
3 
4// 判斷給定的索引鍵中沒有給定的驗證錯誤訊息...
5$response->assertValid(['name', 'email']);

assertInvalid

判斷 Response 中,給定的索引鍵是否有驗證錯誤訊息。該方法可用於檢查以 JSON 格式回傳錯誤訊息,或是將驗證錯誤訊息快閃存入 Session 的 Response:

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

也可以判斷給定的索引鍵是否有特定的錯誤訊息。在判斷是否有特定的錯誤訊息時,可提供完整的訊息,或是其中一段的錯誤訊息:

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

assertViewHas

判斷 Response View 中是否包含給定的一部分資料:

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

若在 assertViewHas 方法中的第二個引數傳入閉包,則可檢查並針對一部分的資料做 Assertion:

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

此外,也可以在 Response 上以陣列變數的形式來存取 View Data,讓我們可以方便地檢查這些值:

1$this->assertEquals('Taylor', $response['name']);
1$this->assertEquals('Taylor', $response['name']);

assertViewHasAll

判斷 Response View 中是否包含一組資料:

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

該方法可用於檢查 View 中是否含有符合給定索引鍵的資料:

1$response->assertViewHasAll([
2 'name',
3 'email',
4]);
1$response->assertViewHasAll([
2 'name',
3 'email',
4]);

或者,也可以判斷是否包含特定的 View Data,且這些資料是否為指定的值:

1$response->assertViewHasAll([
2 'name' => 'Taylor Otwell',
3 'email' => '[email protected],',
4]);
1$response->assertViewHasAll([
2 'name' => 'Taylor Otwell',
3 'email' => '[email protected],',
4]);

assertViewIs

判斷 Route 是否回傳給定的 View:

1$response->assertViewIs($value);
1$response->assertViewIs($value);

assertViewMissing

判斷程式回傳的 Reponse 中,是否不含給定的資料索引鍵:

1$response->assertViewMissing($key);
1$response->assertViewMissing($key);

身份驗證 Assertion

Laravel 也提供了各種與身份驗證相關的 Assertion,讓我們能在專案的 Feature Test 中使用。請注意,這些方法需要在 Test Class 上呼叫,而不是在 getpost 方法回傳的 Illuminate\Testing\TestResponse實體上呼叫。

assertAuthenticated

判斷使用者已登入:

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

assertGuest

判斷使用者是否未登入:

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

assertAuthenticatedAs

判斷是否已登入特定的使用者:

1$this->assertAuthenticatedAs($user, $guard = null);
1$this->assertAuthenticatedAs($user, $guard = null);
翻譯進度
100% 已翻譯
更新時間:
2024年6月30日 上午8:27:00 [世界標準時間]
翻譯人員:
  • cornch
幫我們翻譯此頁

留言

尚無留言

“Laravel” is a Trademark of Taylor Otwell.
The source documentation is released under MIT license. See laravel/docs on GitHub for details.
The translated documentations are released under MIT license. See cornch/laravel-docs-l10n on GitHub for details.