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

閱讀時間約 19 分鐘

在前幾篇文章中,我們介紹了測試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' => '[email protected]',

], [ 'id' => 1,

'name' => 'name1',

'email' => '[email protected]',

], ]; 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' => '[email protected]',

], [ 'id' => 1,

'name' => 'name1',

'email' => '[email protected]',

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

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

->assertJson([

'users' => [

[ 'id' => 1,

'name' => 'name1',

'email' => '[email protected]',

], [ 'id' => 1,

'name' => 'name1',

'email' => '[email protected]',

], ] ]); }}

在以上程式碼中, 我們建立了一個可以取得 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' => '[email protected]',

], [ 'id' => 1,

'name' => 'name1',

'email' => '[email protected]',

], ]; 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 的鍵與值。

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

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

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

本系列文章目錄

8會員
224內容數
歡迎來到 WilliamP 的沙龍天地,在這裡將與各位讀者探討各種主題,包刮高中數學題庫、PHP開發經驗、LINE聊天機器人開發經驗、書摘筆記等,歡迎交流!
留言0
查看全部
發表第一個留言支持創作者!
WilliamP的沙龍 的其他內容
今天會再與大家介紹幾個資料庫 Assertion 函數,與陣列 Assertion 函數。 與前一篇一樣,以下會提到的資料庫 Assertion 函數,並非 PHPUnit 內建,而是由 Laravel 所擴充,因此需注意是否有確實引用到 use Tests\\TestCase 。最後面介紹的2個
前一天與大家分享了幾個通用型 Assertion 函數,今天來為大家介紹幾個 HTTP 相關的 Assertion 函數吧! 今天要介紹的各函數,其使用方式和前一天所介紹的略有不同。以下所列各函數,皆是基於 HTTP Response 來做驗證測試,因此大家會看到 $response = $thi
前一天我們實作了第一個測試,我們學到了第一個 Assert 函數 assertEquals。 今天讓我們來了解其他常用的 Assert 函數吧! 通用型 Assertion 函數 assertEmpty 函數簽名:assertEmpty(mixed $actual[, string $mess
今天我們來寫第一個單元測試吧! 不過在那之前,先讓我們了解單元測試的「3個A」 單元測試3A 所謂的「3個A」,是指以下三個英文單字: Arrange:初始化工作,如準備假資料 Act:執行測試對象 Assert:驗證結果 一個良好的單元測試案例,應該包含以上的結構, 依序執行 Arra
下載與設定 Laradock 首先,讓我們在Home資料夾下,將 Laradock 下載下來: cd ~ && git clone <https://github.com/Laradock/laradock.git> Laradock 將 Laradock 下載回來後,切換到 Laradock
初遇自動化測試 在數年前,我剛從第一份工作離職,轉職到第二份工作, 新工作是在一個大集團的IT部門,職位是後端工程師。 當時集團正準備導入一個由子公司開發的微服務系統, 使用的技術是PHP 8 及 Laravel 9 因為該系 統在子公司運作得不錯, 因此集團高層想將它擴展成,全集團都可使用的規模
今天會再與大家介紹幾個資料庫 Assertion 函數,與陣列 Assertion 函數。 與前一篇一樣,以下會提到的資料庫 Assertion 函數,並非 PHPUnit 內建,而是由 Laravel 所擴充,因此需注意是否有確實引用到 use Tests\\TestCase 。最後面介紹的2個
前一天與大家分享了幾個通用型 Assertion 函數,今天來為大家介紹幾個 HTTP 相關的 Assertion 函數吧! 今天要介紹的各函數,其使用方式和前一天所介紹的略有不同。以下所列各函數,皆是基於 HTTP Response 來做驗證測試,因此大家會看到 $response = $thi
前一天我們實作了第一個測試,我們學到了第一個 Assert 函數 assertEquals。 今天讓我們來了解其他常用的 Assert 函數吧! 通用型 Assertion 函數 assertEmpty 函數簽名:assertEmpty(mixed $actual[, string $mess
今天我們來寫第一個單元測試吧! 不過在那之前,先讓我們了解單元測試的「3個A」 單元測試3A 所謂的「3個A」,是指以下三個英文單字: Arrange:初始化工作,如準備假資料 Act:執行測試對象 Assert:驗證結果 一個良好的單元測試案例,應該包含以上的結構, 依序執行 Arra
下載與設定 Laradock 首先,讓我們在Home資料夾下,將 Laradock 下載下來: cd ~ && git clone <https://github.com/Laradock/laradock.git> Laradock 將 Laradock 下載回來後,切換到 Laradock
初遇自動化測試 在數年前,我剛從第一份工作離職,轉職到第二份工作, 新工作是在一個大集團的IT部門,職位是後端工程師。 當時集團正準備導入一個由子公司開發的微服務系統, 使用的技術是PHP 8 及 Laravel 9 因為該系 統在子公司運作得不錯, 因此集團高層想將它擴展成,全集團都可使用的規模
你可能也想看
Google News 追蹤
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
練習 PHPUnit 測試的撰寫,依序創建Controller、Service,並針對計算邏輯進行單元測試的練習。
Thumbnail
在 Laravel 中的測試中,PHPUnit 和 Mockery 都可以用來創建測試替身(test double),但它們有不同的方式和功能,以下簡單介紹兩種寫法方式。
前言 基本準備差不多了,也能跑自己的測試,再來就是關於測試腳本的核心:元素定位跟動作,本篇會著重介紹 XPATH 定位的部分
Thumbnail
前言 上篇我們成功執行第一個測試案例,從 Python 腳本透過 Appium 控制模擬器點選設定中的電池,下個問題就是怎麼找元件,這時候就要請出 Appium Inspector 了
前言 經過五個小單元的準備,終於可以開始跑第一個測試了,Appium 本身是個工具,可以搭配各種語言,這邊選擇 Python 作為測試腳本語言,以便之後跟 Robot Framework 串接。
前言 前四篇,把主機作業系統跟待測物準備交代完畢,有需要請自行跳轉取用,接下來就是測試工具的部分,這次測試套件使用大名鼎鼎 Appium 2。 選擇 Appium 2 的理由 歷史悠久:Appium 2012 年公開之後,就廣受測試社群愛戴 站在巨人的肩榜上:架構類似 Selenium的主從式架構,
前言 前幾篇聊到作業系統、Docker 跟 Android 容器的準備,再來就是替 Android 容器開啟 Google Play 套件並安裝待測 App 供後續手動或者自動測試使用。
前言 前兩篇把作業系統跟 Docker 安裝講完了,接下來就是 Android 容器的安裝了,這裡選用 ReDroid ,因為它是開源、高效、又便於管理的方案。
Thumbnail
前言 前篇把 Ubuntu 作業系統的安裝跟準備談完了,有需要可以跳回去看。接下來聊容器服務 Docker 的安裝與使用。 Docker 可以應用的場合很多,這次是會用它來模擬 Android 受測裝置
前言 本 App 自動化測試專題,用來記錄自動化 App 測試的各環節,包含環境準備、套件安裝、腳本編寫、執行測試與整合。
Thumbnail
這個秋,Chill 嗨嗨!穿搭美美去賞楓,裝備款款去露營⋯⋯你的秋天怎麼過?秋日 To Do List 等你分享! 秋季全站徵文,我們準備了五個創作主題,參賽還有機會獲得「火烤兩用鍋」,一起來看看如何參加吧~
Thumbnail
美國總統大選只剩下三天, 我們觀察一整週民調與金融市場的變化(包含賭局), 到本週五下午3:00前為止, 誰是美國總統幾乎大概可以猜到60-70%的機率, 本篇文章就是以大選結局為主軸來討論近期甚至到未來四年美股可能的改變
Thumbnail
Faker昨天真的太扯了,中國主播王多多點評的話更是精妙,分享給各位 王多多的點評 「Faker是我們的處境,他是LPL永遠繞不開的一個人和話題,所以我們特別渴望在決賽跟他相遇,去直面我們的處境。 我們曾經稱他為最高的山,最長的河,以為山海就是盡頭,可是Faker用他28歲的年齡...
Thumbnail
練習 PHPUnit 測試的撰寫,依序創建Controller、Service,並針對計算邏輯進行單元測試的練習。
Thumbnail
在 Laravel 中的測試中,PHPUnit 和 Mockery 都可以用來創建測試替身(test double),但它們有不同的方式和功能,以下簡單介紹兩種寫法方式。
前言 基本準備差不多了,也能跑自己的測試,再來就是關於測試腳本的核心:元素定位跟動作,本篇會著重介紹 XPATH 定位的部分
Thumbnail
前言 上篇我們成功執行第一個測試案例,從 Python 腳本透過 Appium 控制模擬器點選設定中的電池,下個問題就是怎麼找元件,這時候就要請出 Appium Inspector 了
前言 經過五個小單元的準備,終於可以開始跑第一個測試了,Appium 本身是個工具,可以搭配各種語言,這邊選擇 Python 作為測試腳本語言,以便之後跟 Robot Framework 串接。
前言 前四篇,把主機作業系統跟待測物準備交代完畢,有需要請自行跳轉取用,接下來就是測試工具的部分,這次測試套件使用大名鼎鼎 Appium 2。 選擇 Appium 2 的理由 歷史悠久:Appium 2012 年公開之後,就廣受測試社群愛戴 站在巨人的肩榜上:架構類似 Selenium的主從式架構,
前言 前幾篇聊到作業系統、Docker 跟 Android 容器的準備,再來就是替 Android 容器開啟 Google Play 套件並安裝待測 App 供後續手動或者自動測試使用。
前言 前兩篇把作業系統跟 Docker 安裝講完了,接下來就是 Android 容器的安裝了,這裡選用 ReDroid ,因為它是開源、高效、又便於管理的方案。
Thumbnail
前言 前篇把 Ubuntu 作業系統的安裝跟準備談完了,有需要可以跳回去看。接下來聊容器服務 Docker 的安裝與使用。 Docker 可以應用的場合很多,這次是會用它來模擬 Android 受測裝置
前言 本 App 自動化測試專題,用來記錄自動化 App 測試的各環節,包含環境準備、套件安裝、腳本編寫、執行測試與整合。