測試 ASP.NET Core API Controller
本文需要您了解ASP.NET Core MVC/Web API, xUnit以及Moq相關知識.
這裡有xUnit和Moq的介紹: https://www.cnblogs.com/cgzl/p/9178672.html#test
Controllers可以說是ASP.NET Core MVC/Web API項目的核心, 它們把整個應用都整合到了一起. 可以說Controllers是非常重要的, 是以我們應該對它們做一些測試.
由于我幾乎隻做API, 是以本文不包括關于MVC功能的測試, 隻包括Controller的API相關功能.
測試一個簡單的Controller
先舉一個簡單點的例子:

這個Controller相對簡單, 它有一個依賴項.
它一個方法, 傳回類型是IActionResult, 又具體分為兩種情況.
測試傳回結果的類型
首先需要new出來一個被測試的RootController, 标準的叫法叫System Under Test(被測試系統). 它需要一個urlHelper作為依賴項, 那就Mock一個即可.
每組測試資料都會走一遍構造函數的.
該測試方法使用的是Theory, 用了4組資料. 執行方法後傳回的結果類型應該實作了IActionResult接口, 這裡可以用Assert.IsAssignableFrom<TExpected>(actual)來判斷.
注: 為了友善, 我使用了resharper.
測試之前一定要重新Build一下.
然後再點選resharper在方法旁邊提供的測試按鈕即可:
從圖可以看出resharper提供了友善快捷的圖示, 在這你可以選擇運作或者調試測試.
測試會通過的, Theory下屬的4組資料将被視為4個單獨的測試:
針對該方法的其它測試
我又添加了兩個測試方法, 來測試該方法的不同路徑及傳回結果:
通常一個測試方法裡應該隻有一個Assert. 但是第二方法裡面有兩個Assert, 這是因為這兩個Assert都是測試的同一個行為, 是以我認為這樣應該是可以的.
Rebuild, 測試:
也是OK的.
看起來針對RootController的GetRoot()方法, 我們好像已經測試了所有可執行的路徑. 讓我們使用測試代碼覆寫率這個功能來确定一下.
點選resharper在測試類旁邊提供的CoverAll按鈕:
随後會出現單元測試視窗和覆寫率視窗.
直接看覆寫率視窗:
可以看到該Controller和方法的覆寫率都是100%了.
來到被測試的RootController裡:
Resharper(實際上是dotCover) 在代碼的左邊顯示出了該行代碼是否已經被測試覆寫, 如果都是綠色的就說明都被覆寫了.
導出覆寫率結果
Resharper的代碼覆寫率結果可以導出多種格式:
例如導出HTML後也可以檢視覆寫率明細:
測試複雜一點的Controller
這個ProductController略微複雜一點, 首先它需要很多依賴項.
看它的POST Action方法, 很多地方需要被測試:
測試ModelState
首先可以測試product為null的情況, 但是這個太簡單了, 我就不啰嗦了.
那就測試ModelState.Invalid情況吧:
為了讓ModelState Invalid, 我手動添加了ModelState的error. 和被測試方法其它必要的參數.
該方法有三個Assert, 首先判定結果類型是否為UnprocessableEntityObjectResult(422狀态碼), 然後再判定傳回結果包含了ModelState的error.
該測試會pass, 并會覆寫這部分相關的代碼:
測試特定方法會被調用
這裡需要使用moq了, 為了讓被測試方法順利跑完, 我設定Mock版的UnitOfWork的SaveAsync()方法會傳回true, (注意這個方法的傳回類型是Task<bool>):
然後通過moq的Verify()方法判定repository的AddProduct()和unitOfWork的SaveAsync()方法分别被調用了.
Build, 測試會pass, 覆寫率目前比較大了(但是覆寫率100%并不能說明代碼沒問題):
模拟SaveAsync()後的實體資料
該項目使用的是EFCore, 在_unitOfWorkSaveAsync()之後, 變量productModel的Id就會有非0值了, 也就是說productModel在_unitOfWorkSaveAsync()方法執行之後發生了變化.
針對這種情況, 我們可以使用moq的Callback()功能:
剛開始為autoMapper的兩次map動作設定了傳回值.
然後在UnitOfWork的SaveAsync()執行後有個Callback()回調, 回調時相當于模拟了EFCore的儲存, 把最新的值賦給了productModel(看被測試代碼), (其實這裡不用Callback也行....).
随後就是一系列的Assert, 判定某些方法是否執行, 傳回類型是否正确, 傳回的資料是否正确等.
Build 測試會通過的:
其它路徑的測試
目前該方法還有兩處地方沒有被覆寫:
可以再寫兩個測試來覆寫它們:
這兩個很簡單, 不多介紹了, 注意這裡使用了async版本的Assert.Throws().
這兩個測試會pass, 最終該方法的代碼覆寫率就達到100%了:
ASP.NET Core Web API Controller的測試就介紹這些吧.