需求說明
金流系統透過打 API 與第三方介接來進行付款,
為了追蹤金流,在打 API 的過程中,業務單位要求要帶著 RequestId 。
再進行付款。
而 RequestId 由另一個專門負責提供 RequestId 的 API 來提供。
整體流程如下:
打 API 取得 RequestId
1
GET {{url}}/api/{{version}}/requestId
組合付款資料與 RequestId
打 API 完成付款
1
POST {{url}}/api/{{version}}/pay/CreditCard/{{transationId}}
第一個 Case,Pay 的時候應該呼叫 GET reguestId 1 次
問題,我需要驗証 HttpClient 呼叫的 url
與次數
。
一開始會寫成這樣,
1 | [ ] |
我本來就預計使用 HttpClient 來呼叫 API,
但是直接使用 HttpClient 會直接產生耦合,
所以我建立一個介面來包裝它。
1 | public interface IHttpClient |
接著馬上建立類別 HttpClientProxy
實作 IHttpClient
,
這個時候我會知道我會使用 GetAsync 的方法,
所以我會讓 IHttpClient
長出這個同名方法,
實作很單純,就是呼叫 HttpClient().GetAsync 方法。
幾個想法,
這樣算是 Proxy Pattern 嗎 ? 我覺得算是:P
另一點,這個階段我會擔心 HttpClient 的問題,
不處理是對的嗎 ?
如果不刻意處理的話 HttpClientProxy 好像會長不出來
1 | public interface IHttpClient |
完成這階段的修改後,我才可以透過 Framework 來 Mock IHttpClient,
寫好的測試如下,順利拿到第一個紅燈:
1 | [ ] |
馬上修改 Production Code ,拿到綠燈。
1 | public class PaymentService |
第二個 Case,Pay 的時候應該呼叫 POST Pay CreditCard 1 次
測試案例:
1 | [ ] |
修改 Production Code
1 | public void Pay() |
重構測試
1 | [ ] |
第三個 Case,Pay 的時候應該先呼叫 Get RequestId 再 POST Pay CreditCard
1 | [ ] |
想法,有了第三個案例,我還需要前面兩個案例嗎 ?
下一步,調整 ShouldPayByCreditCard 的 Assert 邏輯,
原因是實務上我必須將 RequestId 帶入 Post Pay 時的 HttpContent 裡面。
1 | private void ShouldPayByCreditCard() |
Prodouction Code 因而長出 PayEntity
1 | public void Pay() |
Case4 組合資料邏輯
4.1 組合 PayEntity 的邏輯
這次我假設外部的元件已組合好 PayEntity
傳入 PaymentService.Pay 方法,
唯一的組合邏輯就只剩 RequestId。
至於外部的 PayEntity 組合邏輯如何用 TDD 長出 Production Code 可以參考這篇。
1 | private void WhenPay() |
1 | public void Pay(PayEntity payEntity) |
最後,將 api 的 url 也抽成可參數化。
1 | public PaymentServiceTests() |
Production Code
1 | public PaymentService(IHttpClient httpClient, IConfigure configure) |
小結
在 HttpClient 那邊卡蠻久的,用介面的方法包裝起來也不知道是否合適。
網路上有提供許多不同的作法,單元測試的 TDD 好像趨動不太出來 Production Code
是否需要加入整合測試,甚至是透過呼叫 Production Code 去打 API 作端到端測試,來趨動開發 ?
TDD 的 T 是不是不只是 Unit Test 呢 ?
參考
- https://chrissainty.com/unit-testing-with-httpclient/
- https://dotblogs.com.tw/jakeuj/2019/01/25/httpclient
- https://blog.darkthread.net/blog/httpclient-sigleton/
(fin)