近年來的疫情可說是大大影響了我們,「疾管署」Centers for Disease Control, CDC 也成為熱門單位。筆者這次想蹭個熱度,談談軟體測試領域中的 CDC (Consumer-driven Contract) Testing,以及如何在 .NET API 開發中應用。
首先概述一下,在測試金字塔最頂端的 End-to-End 測試,雖然速度最慢,成本最高,但還是在大多數網際網路服務中,除了又笨又沒效率的人工測試之外,最能佔據使用者的目光,也最能吸引開發端投入。
因此這也造就了「使用者驅動的合約測試」,所謂「使用者」,相對於提供者或開發者,指的是 API 或訊息佇列的一方。在多數的開發場景,由系統分析師多次訪談,來回確認以挖掘出使用者真正的需求,並交給開發人員實作,但這個過程不但冗長又不好控制。那有沒有可能由使用者主動提出心中願景呢?如果這種「合約」能夠很容易地產出,對雙方是不是都具有重大意義?理想是如本文一開始的配圖,讓維護成本與時間呈線性,而非指數關係,而且這概念已被廣泛移植到各種程式語言了。聽起來不錯,接下來讓我們看看 .NET 如何實現?
在 cdc.sln 方案中包含了四個專案 Consumer, Consumer.Tests, Provider, Provider.Tests 與一個目錄 Contract,應該不難想像使用者/提供者各負責兩個專案,並共享 Contract 目錄的內容。既然是使用者驅動,那想必是由 Consumer 或 Consumer.Tests 專案開始(如果你信奉 TDD)。這裡再借 Martin Fowler 大師站上的文章,從一個極簡的功能需求說起:
這些敲定後,這次我們不走傳統的 Provider 先行,而是由 Consumer 深入,以進一步提供細節。首先是專案唯一的程式 HelloApiClient.cs:
三大功能需求,在此由使用者角度實現了兩項,諸如路徑、動詞、標頭、內容、狀態碼、例外處理等,但畢竟不是真的做,重點在描述合約。接下來看 Consumer.Tests 專案,首先引入 PactNet 套件參考,然後唯一的程式:
這是一個 Xunit 單元測試,要特別說明的幾行是:
接著是 Provider 這方的程式,也是依行號特別說明:
最後重頭戲在 Provider.Tests 專案,也要引入 PactNet 套件,再加上 PactNet.Output.Xunit。測試程式的部份,UnitTest2.cs 是為了驗證合約是否履行:
如此一來,開發人員除了寫自己的單元測試之外,也可以執行這個額外的「合約驗證」,了解完成度。如果是部份完成的情況,打開合約來看:
假設 20~33, 34~47 這兩段都還沒履行,6~19 已完成功能,所以整份合約的驗證失敗,那要怎麼驗證完成部份有沒有符合合約呢?很簡單,把未完成的部份刪除,另存新檔,並且驗證時指定新檔就可以囉。