> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# HTTP 測試

> Laravel HTTP 測試功能：請求模擬、斷言、認證測試與檔案上傳測試

## 簡介

Laravel 提供豐富的 API 讓你能模擬 HTTP 請求並驗證回應。
不用真的架設 HTTP 伺服器，就能在測試中重現對應用程式的請求。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('應用程式回傳正常回應', function () {
      $response = $this->get('/');

      $response->assertStatus(200);
  });
  ```

  ```php PHPUnit theme={null}
  <?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);
      }
  }
  ```
</CodeGroup>

`get()` 方法對應用程式送出 `GET` 請求，`assertStatus()` 驗證回應的 HTTP 狀態碼。

<Info>
  執行測試期間，CSRF 中介軟體會自動停用，不必在測試中手動關閉。
</Info>

## 建立請求

在測試中可用 `get`、`post`、`put`、`patch`、`delete` 送出請求。
這些方法不會實際發送網路請求，而是在應用程式內部模擬。
回傳值為 `Illuminate\Testing\TestResponse` 實例，提供大量斷言方法。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('基本的請求', function () {
      $response = $this->get('/');

      $response->assertStatus(200);
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use Tests\TestCase;

  class ExampleTest extends TestCase
  {
      public function test_a_basic_request(): void
      {
          $response = $this->get('/');

          $response->assertStatus(200);
      }
  }
  ```
</CodeGroup>

<Warning>
  建議每個測試方法內原則上僅送出一次請求。同一測試中發送多次請求可能導致非預期行為。
</Warning>

### 自訂請求標頭

用 `withHeaders()` 自訂請求標頭。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('附標頭的請求', function () {
      $response = $this->withHeaders([
          'X-Header' => 'Value',
      ])->post('/user', ['name' => 'Sally']);

      $response->assertStatus(201);
  });
  ```

  ```php PHPUnit theme={null}
  <?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);
      }
  }
  ```
</CodeGroup>

### Cookie

以 `withCookie()` 或 `withCookies()` 在請求前設定 cookie 值。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('附 cookie 的請求', function () {
      $response = $this->withCookie('color', 'blue')->get('/');

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

  ```php PHPUnit theme={null}
  <?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('/');
      }
  }
  ```
</CodeGroup>

### Session 與認證

用 `withSession()` 在請求前設定 session 資料。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('附 session 的請求', function () {
      $response = $this->withSession(['banned' => false])->get('/');
  });
  ```

  ```php PHPUnit theme={null}
  <?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('/');
      }
  }
  ```
</CodeGroup>

用 `actingAs()` 可以以已認證使用者身份送出請求，常搭配模型工廠使用。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use App\Models\User;

  test('需要認證的動作', function () {
      $user = User::factory()->create();

      $response = $this->actingAs($user)
          ->withSession(['banned' => false])
          ->get('/');
  });
  ```

  ```php PHPUnit theme={null}
  <?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('/');
      }
  }
  ```
</CodeGroup>

在 `actingAs()` 的第 2 個參數指定 guard 名稱，可用該 guard 認證。該 guard 在測試期間會成為預設。

```php theme={null}
$this->actingAs($user, 'web');
```

若要以未認證狀態發送請求，用 `actingAsGuest()`。

```php theme={null}
$this->actingAsGuest();
```

### 除錯回應

若想在測試中檢視回應內容，可用 `dump`、`dumpHeaders`、`dumpSession`。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('回應除錯', function () {
      $response = $this->get('/');

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

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use Tests\TestCase;

  class ExampleTest extends TestCase
  {
      public function test_debug_response(): void
      {
          $response = $this->get('/');

          $response->dump();
          $response->dumpHeaders();
          $response->dumpSession();
      }
  }
  ```
</CodeGroup>

若要中止執行，可用 `dd`、`ddHeaders`、`ddBody`、`ddJson`、`ddSession`。

```php theme={null}
$response->dd();
$response->ddHeaders();
$response->ddBody();
$response->ddJson();
$response->ddSession();
```

### 例外測試

要測試是否會拋出特定例外，可用 `Exceptions` Facade。

<CodeGroup>
  ```php Pest theme={null}
  <?php

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

  test('拋出例外', 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 PHPUnit theme={null}
  <?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.';
          });
      }
  }
  ```
</CodeGroup>

確認未拋出例外，用 `assertNotReported` 或 `assertNothingReported`。

```php theme={null}
Exceptions::assertNotReported(InvalidOrderException::class);

Exceptions::assertNothingReported();
```

若要停用例外處理送出請求，用 `withoutExceptionHandling()`。

```php theme={null}
$response = $this->withoutExceptionHandling()->get('/');
```

要測試閉包中的程式碼是否拋出例外，用 `assertThrows()`。

```php theme={null}
$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    OrderInvalid::class
);

// 若要檢查例外內容
$this->assertThrows(
    fn () => (new ProcessOrder)->execute(),
    fn (OrderInvalid $e) => $e->orderId() === 123
);
```

確認不會拋出例外，用 `assertDoesntThrow()`。

```php theme={null}
$this->assertDoesntThrow(fn () => (new ProcessOrder)->execute());
```

## JSON API 的測試

Laravel 提供多種 JSON API 測試輔助方法。
`json`、`getJson`、`postJson`、`putJson`、`patchJson`、`deleteJson`、`optionsJson` 皆可送出 JSON 請求。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('送出 API 請求', function () {
      $response = $this->postJson('/api/user', ['name' => 'Sally']);

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

  ```php PHPUnit theme={null}
  <?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,
              ]);
      }
  }
  ```
</CodeGroup>

JSON 回應資料可用陣列變數方式存取。

<CodeGroup>
  ```php Pest theme={null}
  expect($response['created'])->toBeTrue();
  ```

  ```php PHPUnit theme={null}
  $this->assertTrue($response['created']);
  ```
</CodeGroup>

<Info>
  `assertJson()` 會把回應轉為陣列，並驗證所指定陣列是否包含在 JSON 中。JSON 中其他屬性即使存在，只要指定的片段有出現，測試就會通過。
</Info>

### 完全比對的斷言

用 `assertExactJson()` 可驗證回傳 JSON 與指定陣列**完全一致**。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('完全比對的 JSON 斷言', function () {
      $response = $this->postJson('/user', ['name' => 'Sally']);

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

  ```php PHPUnit theme={null}
  <?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,
              ]);
      }
  }
  ```
</CodeGroup>

### JSON path 的斷言

用 `assertJsonPath()` 檢驗特定路徑上的資料。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('JSON path 斷言', function () {
      $response = $this->postJson('/user', ['name' => 'Sally']);

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

  ```php PHPUnit theme={null}
  <?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');
      }
  }
  ```
</CodeGroup>

也可以傳入閉包做更彈性的檢查。

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

### 流暢的 JSON 測試

`assertJson()` 傳入閉包時，可用 `AssertableJson` 實例以流暢方式撰寫斷言。

<CodeGroup>
  ```php Pest theme={null}
  use Illuminate\Testing\Fluent\AssertableJson;

  test('流暢的 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 PHPUnit theme={null}
  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('victoria@gmail.com'))
                  ->whereNot('status', 'pending')
                  ->missing('password')
                  ->etc()
          );
  }
  ```
</CodeGroup>

<Tip>
  `etc()` 方法允許存在非斷言中的其他屬性。若未加 `etc()`，一旦有未斷言的屬性存在，測試會失敗。這可避免無意間將機密資訊納入回應。
</Tip>

檢查屬性存在 / 不存在，用 `has()` 與 `missing()`。

```php theme={null}
$response->assertJson(fn (AssertableJson $json) =>
    $json->has('data')
        ->missing('message')
);
```

一次檢查多個屬性可用 `hasAll()` 或 `missingAll()`。

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

#### JSON 集合的斷言

若路由回傳含多個項目的 JSON，可用 `has()` 檢驗數量與內容。

```php theme={null}
$response
    ->assertJson(fn (AssertableJson $json) =>
        $json->has(3)
            ->first(fn (AssertableJson $json) =>
                $json->where('id', 1)
                    ->where('name', 'Victoria Faith')
                    ->missing('password')
                    ->etc()
            )
    );
```

要對所有項目套用相同斷言，用 `each()`。

```php theme={null}
$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 的型別斷言

以 `whereType()` 或 `whereAllType()` 檢查屬性型別。

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

也可以用 `|` 指定多個型別，其中之一符合即算通過。

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

可用的型別有 `string`、`integer`、`double`、`boolean`、`array`、`null`。

## 認證測試

用 `actingAs()` 以已認證使用者身份送出請求。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use App\Models\User;
  use Illuminate\Foundation\Testing\RefreshDatabase;

  uses(RefreshDatabase::class);

  test('已認證使用者可看儀表板', function () {
      $user = User::factory()->create();

      $response = $this->actingAs($user)->get('/dashboard');

      $response->assertStatus(200);
  });

  test('未認證使用者會被重新導向', function () {
      $response = $this->get('/dashboard');

      $response->assertRedirect('/login');
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use App\Models\User;
  use Illuminate\Foundation\Testing\RefreshDatabase;
  use Tests\TestCase;

  class DashboardTest extends TestCase
  {
      use RefreshDatabase;

      public function test_authenticated_users_can_view_the_dashboard(): void
      {
          $user = User::factory()->create();

          $response = $this->actingAs($user)->get('/dashboard');

          $response->assertStatus(200);
      }

      public function test_unauthenticated_users_are_redirected(): void
      {
          $response = $this->get('/dashboard');

          $response->assertRedirect('/login');
      }
  }
  ```
</CodeGroup>

要以特定 guard 認證，於第 2 個參數指定：

```php theme={null}
$this->actingAs($user, 'api');
```

### 使用者註冊流程測試範例

實際測試使用者註冊端點的範例：

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use App\Models\User;
  use Illuminate\Foundation\Testing\RefreshDatabase;

  uses(RefreshDatabase::class);

  test('使用者可以註冊', function () {
      $response = $this->post('/register', [
          'name'                  => 'Test User',
          'email'                 => 'test@example.com',
          'password'              => 'password',
          'password_confirmation' => 'password',
      ]);

      $response->assertRedirect('/dashboard');
      $this->assertDatabaseHas('users', ['email' => 'test@example.com']);
  });

  test('重複 email 不可註冊', function () {
      User::factory()->create(['email' => 'test@example.com']);

      $response = $this->post('/register', [
          'name'                  => 'Test User',
          'email'                 => 'test@example.com',
          'password'              => 'password',
          'password_confirmation' => 'password',
      ]);

      $response->assertSessionHasErrors('email');
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use App\Models\User;
  use Illuminate\Foundation\Testing\RefreshDatabase;
  use Tests\TestCase;

  class RegistrationTest extends TestCase
  {
      use RefreshDatabase;

      public function test_user_can_register(): void
      {
          $response = $this->post('/register', [
              'name'                  => 'Test User',
              'email'                 => 'test@example.com',
              'password'              => 'password',
              'password_confirmation' => 'password',
          ]);

          $response->assertRedirect('/dashboard');
          $this->assertDatabaseHas('users', ['email' => 'test@example.com']);
      }

      public function test_duplicate_email_cannot_register(): void
      {
          User::factory()->create(['email' => 'test@example.com']);

          $response = $this->post('/register', [
              'name'                  => 'Test User',
              'email'                 => 'test@example.com',
              'password'              => 'password',
              'password_confirmation' => 'password',
          ]);

          $response->assertSessionHasErrors('email');
      }
  }
  ```
</CodeGroup>

## Session 測試

用 `withSession()` 事先設定 session，用 `assertSessionHas()` 檢驗 session 是否有指定值。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('驗證 session 值', function () {
      $response = $this->withSession(['locale' => 'ja'])->get('/');

      $response->assertSessionHas('locale', 'ja');
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use Tests\TestCase;

  class SessionTest extends TestCase
  {
      public function test_session_value(): void
      {
          $response = $this->withSession(['locale' => 'ja'])->get('/');

          $response->assertSessionHas('locale', 'ja');
      }
  }
  ```
</CodeGroup>

### Session 斷言一覽

| 方法                                      | 說明                  |
| --------------------------------------- | ------------------- |
| `assertSessionHas($key, $value)`        | session 中有指定 key 與值 |
| `assertSessionHasAll([...])`            | session 中有多個 key 與值 |
| `assertSessionHasErrors($keys)`         | session 中含指定的驗證錯誤   |
| `assertSessionHasErrorsIn($bag, $keys)` | 指定錯誤袋中含錯誤           |
| `assertSessionHasNoErrors()`            | session 中無驗證錯誤      |
| `assertSessionDoesntHaveErrors()`       | session 中無驗證錯誤      |
| `assertSessionMissing($key)`            | session 中無指定 key    |

## 檔案上傳的測試

以 `Illuminate\Http\UploadedFile` 的 `fake()` 產生虛擬檔案或圖片。
搭配 `Storage` Facade 的 `fake()` 可輕鬆測試檔案上傳。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  use Illuminate\Http\UploadedFile;
  use Illuminate\Support\Facades\Storage;

  test('可以上傳頭像', function () {
      Storage::fake('avatars');

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

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

      Storage::disk('avatars')->assertExists($file->hashName());
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use Illuminate\Http\UploadedFile;
  use Illuminate\Support\Facades\Storage;
  use Tests\TestCase;

  class AvatarTest 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());
      }
  }
  ```
</CodeGroup>

確認檔案不存在可用 `assertMissing()`。

```php theme={null}
Storage::disk('avatars')->assertMissing('missing.jpg');
```

### 自訂虛擬檔案

可指定圖片尺寸或檔案大小，適合測試驗證規則。

```php theme={null}
// 指定寬 / 高 / 大小（大小單位為 KB）
UploadedFile::fake()->image('avatar.jpg', $width, $height)->size(100);

// 建立任意檔案類型
UploadedFile::fake()->create('document.pdf', $sizeInKilobytes);

// 明確指定 MIME 類型
UploadedFile::fake()->create(
    'document.pdf', $sizeInKilobytes, 'application/pdf'
);
```

## 視圖測試

不需模擬 HTTP 請求，也能直接渲染視圖進行測試。
`view()` 方法接收視圖名稱與可選的資料陣列，回傳 `Illuminate\Testing\TestView` 實例。

<CodeGroup>
  ```php Pest theme={null}
  <?php

  test('welcome 視圖可渲染', function () {
      $view = $this->view('welcome', ['name' => 'Taylor']);

      $view->assertSee('Taylor');
  });
  ```

  ```php PHPUnit theme={null}
  <?php

  namespace Tests\Feature;

  use Tests\TestCase;

  class ViewTest extends TestCase
  {
      public function test_a_welcome_view_can_be_rendered(): void
      {
          $view = $this->view('welcome', ['name' => 'Taylor']);

          $view->assertSee('Taylor');
      }
  }
  ```
</CodeGroup>

`TestView` 類別提供的斷言方法：

| 方法                            | 說明           |
| ----------------------------- | ------------ |
| `assertSee($value)`           | 視圖包含指定字串     |
| `assertSeeInOrder([...])`     | 視圖依序包含指定字串   |
| `assertSeeText($value)`       | 視圖文字包含指定字串   |
| `assertSeeTextInOrder([...])` | 視圖文字依序包含指定字串 |
| `assertDontSee($value)`       | 視圖不含指定字串     |
| `assertDontSeeText($value)`   | 視圖文字不含指定字串   |

要以字串取得已渲染視圖內容，可將 `TestView` 實例轉為字串。

```php theme={null}
$contents = (string) $this->view('welcome');
```

要將驗證錯誤傳入視圖，可用 `withViewErrors()`。

```php theme={null}
$view = $this->withViewErrors([
    'name' => ['請輸入有效名稱。']
])->view('form');

$view->assertSee('請輸入有效名稱。');
```

### 元件測試

以 `blade()` 渲染原始 Blade 樣板字串。

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

$view->assertSee('Taylor');
```

以 `component()` 渲染 Blade 元件，回傳 `Illuminate\Testing\TestComponent` 實例。

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

$view->assertSee('Taylor');
```

## 回應斷言一覽

`Illuminate\Testing\TestResponse` 類別的主要斷言方法：

### HTTP 狀態

| 方法                               | 說明                        |
| -------------------------------- | ------------------------- |
| `assertOk()`                     | 200 OK                    |
| `assertCreated()`                | 201 Created               |
| `assertAccepted()`               | 202 Accepted              |
| `assertNoContent($status = 204)` | 204 No Content            |
| `assertBadRequest()`             | 400 Bad Request           |
| `assertUnauthorized()`           | 401 Unauthorized          |
| `assertForbidden()`              | 403 Forbidden             |
| `assertNotFound()`               | 404 Not Found             |
| `assertUnprocessable()`          | 422 Unprocessable Entity  |
| `assertTooManyRequests()`        | 429 Too Many Requests     |
| `assertInternalServerError()`    | 500 Internal Server Error |
| `assertStatus($code)`            | 指定狀態碼                     |
| `assertSuccessful()`             | 2xx 系列                    |
| `assertClientError()`            | 4xx 系列                    |
| `assertServerError()`            | 5xx 系列                    |

### 重新導向

| 方法                                            | 說明              |
| --------------------------------------------- | --------------- |
| `assertRedirect($uri)`                        | 重新導向到指定 URI     |
| `assertRedirectContains($string)`             | 重新導向 URI 包含指定字串 |
| `assertRedirectToRoute($name, $params)`       | 重新導向至指定路由       |
| `assertRedirectToSignedRoute($name, $params)` | 重新導向至指定的簽章路由    |
| `assertRedirectBack()`                        | 導回上一個 URL       |

### 內容

| 方法                                   | 說明         |
| ------------------------------------ | ---------- |
| `assertSee($value, $escaped = true)` | 回應含指定值     |
| `assertSeeInOrder(array $values)`    | 依序含指定值     |
| `assertSeeText($value)`              | 含指定文字      |
| `assertDontSee($value)`              | 不含指定值      |
| `assertDontSeeText($value)`          | 不含指定文字     |
| `assertContent($value)`              | 回應內文與指定值一致 |
| `assertDownload($filename)`          | 為下載回應      |

### JSON

| 方法                                         | 說明                   |
| ------------------------------------------ | -------------------- |
| `assertJson(array $data)`                  | JSON 含指定資料           |
| `assertExactJson(array $data)`             | JSON 與指定資料完全一致       |
| `assertJsonPath($path, $value)`            | 指定路徑的值一致             |
| `assertJsonMissingPath($path)`             | 指定路徑不存在              |
| `assertJsonStructure(array $structure)`    | JSON 具指定結構           |
| `assertJsonCount($count, $key)`            | 指定 key 的 JSON 具指定元素數 |
| `assertJsonFragment(array $data)`          | JSON 含指定片段           |
| `assertJsonMissing(array $data)`           | JSON 不含指定資料          |
| `assertJsonIsArray()`                      | JSON 為陣列             |
| `assertJsonIsObject()`                     | JSON 為物件             |
| `assertJsonValidationErrors($keys)`        | 含指定 key 的驗證錯誤        |
| `assertJsonMissingValidationErrors($keys)` | 不含指定 key 的驗證錯誤       |

### 標頭與 Cookie

| 方法                                  | 說明            |
| ----------------------------------- | ------------- |
| `assertHeader($headerName, $value)` | 含指定標頭         |
| `assertHeaderMissing($headerName)`  | 不含指定標頭        |
| `assertCookie($name, $value)`       | 含指定 cookie    |
| `assertCookieExpired($name)`        | 指定 cookie 已過期 |
| `assertCookieMissing($name)`        | 不含指定 cookie   |
| `assertPlainCookie($name, $value)`  | 含指定未加密 cookie |

### 視圖

| 方法                              | 說明       |
| ------------------------------- | -------- |
| `assertViewIs($value)`          | 回傳指定視圖   |
| `assertViewHas($key, $value)`   | 視圖中含指定資料 |
| `assertViewHasAll(array $data)` | 視圖中含多筆資料 |
| `assertViewMissing($key)`       | 視圖中無指定資料 |

### 驗證

| 方法                     | 說明           |
| ---------------------- | ------------ |
| `assertValid($keys)`   | 指定 key 無驗證錯誤 |
| `assertInvalid($keys)` | 指定 key 有驗證錯誤 |

## 實踐 TDD 的要點

<Tip>
  HTTP 測試與 TDD（測試驅動開發）非常契合。留意以下要點會更有效。
</Tip>

<AccordionGroup>
  <Accordion title="從 Feature 測試開始">
    HTTP 測試放在 `tests/Feature/` 目錄。從應用程式外部視角先定義行為（請求→回應），能讓待實作功能清晰。
  </Accordion>

  <Accordion title="使用 RefreshDatabase">
    需使用資料庫的測試請採用 `RefreshDatabase` trait。每測試後重置資料庫，可避免測試間資料干擾。維持測試獨立性，能構築不受執行順序影響的穩定測試套件。
  </Accordion>

  <Accordion title="積極使用工廠">
    使用如 `User::factory()->create()` 等模型工廠可簡化測試資料建立。定義工廠 state 讓具備特定狀態的模型建立更容易，測試可讀性亦提升。
  </Accordion>

  <Accordion title="一測試一斷言">
    每個測試方法盡量只驗證一件事，測試失敗時原因更好定位。留意「Arrange（準備）→ Act（執行）→ Assert（驗證）」的 AAA 模式，測試會更易讀。
  </Accordion>

  <Accordion title="別忘記認證測試">
    需認證的路由請務必同時測「已認證」與「未認證」情境，可儘早發現安全問題。
  </Accordion>
</AccordionGroup>

## 相關頁面

<Card title="測試入門" icon="flask" href="/zh-TW/testing">
  瞭解 Laravel 測試的基本寫法與 `php artisan test` 的使用。
</Card>


## Related topics

- [測試入門](/zh-TW/testing.md)
- [用 Pest PHP 開始 Laravel 測試](/zh-TW/blog/pest-introduction.md)
- [Mock](/zh-TW/mocking.md)
- [使用 Pest 進行進階測試](/zh-TW/advanced/testing-pest.md)
- [引擎 API 整合應用開發指南 - VOICEVOX for Laravel](/zh-TW/packages/laravel-voicevox/app-guide.md)
