iT邦幫忙

2026 iThome 鐵人賽

DAY 18
0
Software Development

同一套 Laravel 系統,測試怎麼寫才不會說謊系列 第 18

Day 18:SOAP Client 測試——不真的打外部系統也能驗證邏輯

  • 分享至 

  • xImage
  •  

前言

「要測試一個包著 SOAP 協議的 Client 類別,是不是一定要連上真實的外部服務才能驗證?」

前兩天講了測試替身的設計理念跟錄影帶的組織方式,今天要具體看一次完整的實作案例——這個系統怎麼把「注入 Fake」跟「驗證回傳結構」串成一套完整的單元測試流程,全程不真的打任何外部服務。

今日目標

  • 看一支 SOAP Client 單元測試的完整寫法
  • 理解「建構子注入」這個設計選擇,為什麼是測試替身能派上用場的關鍵
  • 學會驗證回傳物件結構的具體手法
  • 認識時間相依邏輯在測試裡怎麼固定住

從建構子注入開始

這個系統負責跟外部校務系統的資料查詢服務對接的 Client 類別,建構子接受一個實作了 Caller 介面的物件,而不是在類別內部自己 new 一個具體的 SOAP 呼叫器實例。這個設計選擇,正是 Day 16 講的「依賴介面」原則的具體落地——測試的時候,只要把 Day 16 提到的 FakeCaller 傳進建構子,就能完全取代真實的 SOAP 連線。

測試怎麼寫

如果沒有介面注入,測試會被迫依賴真實連線

class DataGatewayClient
{
    public function query(string $method, array $params)
    {
        $caller = new RealSoapCaller($this->wsdl); // 寫死在類別內部
        return $caller->call($method, $params);
    }
}

這種寫法沒辦法在測試裡替換掉真實的 SOAP 連線,要嘛測試被迫連上真實外部系統(慢、不穩定、依賴網路),要嘛乾脆放棄測試這段邏輯。

建構子注入讓測試替身可以直接介入

test('queries data gateway and returns structured response', function () {
    $caller = new FakeCaller(cassette: 'campus-api/data-gateway/query');
    $client = new DataGatewayClient($caller);

    $response = $client->query('GetAccount', ['id' => 'test-001']);

    expect(array_keys(get_object_vars($response)))
        ->toBe(['account', 'name', 'department', 'status']);
});

FakeCaller 傳進 DataGatewayClient 的建構子,呼叫查詢方法之後,不驗證回傳值裡每個欄位的實際內容(那是外部系統決定的,不是這段程式碼要負責的事),而是驗證回傳物件的欄位結構符合預期——這段程式碼真正的責任,是把 SOAP 回應正確轉換成一個型別明確的物件,欄位結構對不對,才是這段程式碼自己該負責的事。

固定住時間相依的部分

這個 Client 類別的查詢邏輯裡,有一段跟目前時間有關的判斷(例如查詢的有效期間計算)。測試在執行前,會先用 Carbon 的測試工具把「現在時間」固定在一個確定的時間點,確保測試結果不會因為「今天到底是幾號」而每次執行都不一樣。這個手法會在 Day 24 更完整地展開,這裡先點名:任何跟「現在幾點」有關的邏輯,測試時都該先把時間鎖定住,不然測試會變成一個看執行日期臉色的不穩定測試。

這個流程串起了前三天講的東西

把 Day 16 的測試替身設計、Day 17 的錄影帶組織方式、還有今天的建構子注入手法放在一起看,會發現這是一套完整的鏈路:業務邏輯依賴介面(讓替換變得可能)→ 錄一份真實流量存成有意義命名的 cassette(讓假資料有明確來源)→ 用建構子注入把替身塞進去(讓測試不用真的連線)→ 驗證回傳結構而不是回傳內容(測自己該負責的部分,不測外部系統決定的部分)。這四個環節缺一不可,少了任何一環,都會讓「不真的打外部系統也能驗證邏輯」這件事變得做不到。

今日思考題

回想你手上專案裡跟外部服務對接的類別,建構子是接受一個介面,還是在內部直接 new 一個具體的實作?如果是後者,要幫它寫一支不依賴真實連線的單元測試,需要先做哪一步調整?

今日重點回顧

  • SOAP Client 類別用建構子注入接受 Caller 介面,讓測試替身可以直接替換真實連線
  • 測試驗證的是回傳物件的欄位結構,不是欄位的實際內容——結構是這段程式碼自己的責任
  • 跟時間有關的邏輯,測試前要先用 Carbon 測試工具鎖定「現在時間」
  • 依賴介面、有意義命名的錄影帶、建構子注入、驗證結構而非內容,四個環節串成完整鏈路

明日預告

明天要看另一支測試得很紮實的單元測試——但要跟 Day 14 那支被跳過的登入測試對照著看,理解「底層元件測得仔細」不代表「整合起來的行為有安全網」。


上一篇
Day 17:VCR 測試法總覽——23 個 cassette 檔案怎麼分類組織
下一篇
Day 19:SSO 登入的單元測試很紮實——但要跟 Day 14 對照著看
系列文
同一套 Laravel 系統,測試怎麼寫才不會說謊19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言