2023-12-18|閱讀時間 ‧ 約 38 分鐘

PHPUnit 自動化測試大作戰【CH07】

在前幾篇文章中,我們介紹了測試3A原則,也介紹了許多 Assertion 函數,今天就讓我們實際演練吧!

過去的經驗中,最常用自動化測試來測式的對象,大概就是API了,而前後端分離也是目前 Web 開發界常用的模式,因此我們就以 API 測試來演練吧!

驗證HTTP Status Code

HTTP Status Code 的驗證,應該是最常遇到的 API 測試情境了,其實前面的 Assertion 函數範例中也有展示部分使用方式了,這次讓我們更完整地演練吧!

提醒一點,由於以下的測試會牽涉到資料庫,因此大家要再檢查一下 phpunit.xml 中的資料庫連線設定是否已填上,詳情可參考前Day 6文章內容。

另值得一提的是,為了方便取用路由路徑,在設置路由時,通常都會將路由命名。

案例1:200 — 索取使用者資料

  • routes/api.php
<?php

use App\Models\User;

use Illuminate\Http\Request;

use Illuminate\Support\Facades\Route;

Route::get('/users', function (Request $request) {

$users = User::all();

return response()->json([

'users' => $users,

]);})->name('get.user.list');
  • tests/Feature/HttpStatusCodeTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;

use Tests\TestCase;

class HttpStatusCodeTest extends TestCase{

use RefreshDatabase;

public function testCanGetUserListWhenEmpty() {

$response = $this->get(route('get.user.list'));

$response->assertOk();

// 也可以寫成以下串聯寫法

$this->get(route('get.user.list'))

->assertOk();

}}

在以上程式碼中, users 資料表尚無任何User資料,但取得User清單的端點也應該要能正常回應,透過以上測試程式碼,可以驗證此行為是否正常運作。

案例2:404 — 索取不存在之使用者資料

  • routes/api.php
<?php

use App\Models\User;

use Illuminate\Http\Request;

use Illuminate\Support\Facades\Route;

Route::get('/users/{id}', function (Request $request, $id) {

$user = User::find($id);

if (empty($user)) {

return response()->json([

'error' => 'Not Found',

], 404);

} return response()->json([

'user' => $user,

]);})->where('id', '[0-9]?')->name('get.user');
  • tests/Feature/HttpStatusCodeTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;

use Tests\TestCase;

class HttpStatusCodeTest extends TestCase{

use RefreshDatabase;

public function testCanGetNotFoundWhenNoGivenUser() {

$response = $this->get(route('get.user', ['id' => 1,]));

$response->assertNotFound();

// 也可以寫成以下串聯寫法

$this->get(route('get.user', ['id' => 1,]))

->assertNotFound();

}}

在以上程式碼中, users 資料表尚無任何User資料,因此當嘗試取得 id=1 之 User 資源時,應會找不到,也就是404,透過以上測試程式碼,可以驗證此行為是否正常運作。

案例3:422 — 請求驗證未通過

  • routes/api.php
<?php

use App\Models\User;

use Illuminate\Http\Request;

use Illuminate\Support\Facades\Route;

Route::post('/users', function (Request $request) {

$request->validate([

'name' => 'required|string',

'email' => 'required|email',

'password' => 'required|string',

]); $user = User::create($request->all());

return response()->json([

'user' => $user,

]);})->name('store.user');
  • tests/Feature/HttpStatusCodeTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;

use Tests\TestCase;

class HttpStatusCodeTest extends TestCase{

use RefreshDatabase;

public function testCanResponseUnprocessableWhenInvalidRequest() {

$response = $this->post(

route('store.user'),

['name' => ''],

['Accept' => 'application/json']

); $response->assertUnprocessable();

// 也可以寫成以下串聯寫法

$this->post(

route('store.user'),

['name' => ''],

['Accept' => 'application/json']

) ->assertUnprocessable();

}}

在以上程式碼中, 我們建立了一個可以建立 User 資料的路由端點,與實作建立資料流程,並在這當中建立了驗證請求資料的邏輯,而在下方的測試案例中,我們測試了「當請求內容有缺失時,應回應 422 Unprocessable Entity」這個行為是否正常運作。

驗證Response JSON

除了前面介紹的 HTTP Status Code 驗證,在測試 API 時,另一個常常需要做驗證的部分,應該就是回應JSON的內容本身了,以下就讓我們來看看實例吧!

案例1:驗證回應JSON內容

  • routes/api.php
<?php

use Illuminate\Http\Request;

use Illuminate\Support\Facades\Route;

Route::get('/users', function (Request $request) {

// 這邊先用假的 User 資料

$users = [

[ 'id' => 1,

'name' => 'name1',

'email' => 'user1@email.com',

], [ 'id' => 1,

'name' => 'name1',

'email' => 'user1@email.com',

], ]; return response()->json([

'users' => $users,

]);})->name('get.user.list');
  • tests/Feature/HttpStatusCodeTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;

use Tests\TestCase;

class HttpStatusCodeTest extends TestCase{

use RefreshDatabase;

public function testCanGetUserJson() {

$response = $this->get(route('get.user.list'));

$response->assertJson([

'users' => [

[ 'id' => 1,

'name' => 'name1',

'email' => 'user1@email.com',

], [ 'id' => 1,

'name' => 'name1',

'email' => 'user1@email.com',

], ] ]); // 也可以寫成以下串聯寫法

$this->get(route('get.user.list'))

->assertJson([

'users' => [

[ 'id' => 1,

'name' => 'name1',

'email' => 'user1@email.com',

], [ 'id' => 1,

'name' => 'name1',

'email' => 'user1@email.com',

], ] ]); }}

在以上程式碼中, 我們建立了一個可以取得 User 清單的端點,並且驗證在呼叫此 API 後,其回應的JSON是否符合預期。

案例2:驗證回應JSON結構

  • routes/api.php
<?php

use Illuminate\Http\Request;

use Illuminate\Support\Facades\Route;

Route::get('/users', function (Request $request) {

// 這邊先用假的 User 資料

$users = [

[ 'id' => 1,

'name' => 'name1',

'email' => 'user1@email.com',

], [ 'id' => 1,

'name' => 'name1',

'email' => 'user1@email.com',

], ]; return response()->json([

'users' => $users,

]);})->name('get.user.list');
  • tests/Feature/HttpStatusCodeTest.php
<?php

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;

use Tests\TestCase;

class HttpStatusCodeTest extends TestCase{

use RefreshDatabase;

public function testCanGetUserJsonStructure() {

$response = $this->get(route('get.user.list'));

// '*' 代表是每陣列元素

// 此寫法相當於驗證 'users' 是一個陣列

// 每個陣列元素是一個物件

// 每個物件有 id, name, email 這幾個欄位

$response->assertJsonStructure([

'users' => [

'*' => [

'id',

'name',

'email'

], ], ]); // 也可以寫成以下串聯寫法

$this->get(route('get.user.list'))

->assertJsonStructure([

'users' => [

'*' => [

'id',

'name',

'email'

], ], ]); }}

另一種常見的情況,是只驗證 JSON 結構,而略過驗證 JSON 的鍵與值。

以上就是今天的介紹,大家可以多加練習看看!

下一篇讓我們來練習資料庫測試吧!

如果您喜歡這篇文章,歡迎加入追蹤以接收新文章通知 😄

本系列文章目錄

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.