本文須要您瞭解ASP.NET Core MVC/Web API, xUnit以及Moq相關知識.html
這裏有xUnit和Moq的介紹: http://www.javashuo.com/article/p-rqwpmfqu-gm.htmlapp
Controllers能夠說是ASP.NET Core MVC/Web API項目的核心, 它們把整個應用都整合到了一塊兒. 能夠說Controllers是很是重要的, 因此咱們應該對它們作一些測試.async
因爲我幾乎只作API, 因此本文不包括關於MVC功能的測試, 只包括Controller的API相關功能.函數
先舉一個簡單點的例子:單元測試
這個Controller相對簡單, 它有一個依賴項.測試
它一個方法, 返回類型是IActionResult, 又具體分爲兩種狀況.ui
首先須要new出來一個被測試的RootController, 標準的叫法叫System Under Test(被測試系統). 它須要一個urlHelper做爲依賴項, 那就Mock一個便可.url
每組測試數據都會走一遍構造函數的.spa
該測試方法使用的是Theory, 用了4組數據. 執行方法後返回的結果類型應該實現了IActionResult接口, 這裏能夠用Assert.IsAssignableFrom<TExpected>(actual)來判斷.3d
注: 爲了方便, 我使用了resharper.
測試以前必定要從新Build一下.
而後再點擊resharper在方法旁邊提供的測試按鈕便可:
從圖能夠看出resharper提供了方便快捷的圖標, 在這你能夠選擇運行或者調試測試.
測試會經過的, Theory下屬的4組數據將被視爲4個單獨的測試:
我又添加了兩個測試方法, 來測試該方法的不一樣路徑及返回結果:
一般一個測試方法裏應該只有一個Assert. 可是第二方法裏面有兩個Assert, 這是由於這兩個Assert都是測試的同一個行爲, 因此我認爲這樣應該是能夠的.
Rebuild, 測試:
也是OK的.
看起來針對RootController的GetRoot()方法, 咱們好像已經測試了全部可執行的路徑. 讓咱們使用測試代碼覆蓋率這個功能來肯定一下.
點擊resharper在測試類旁邊提供的CoverAll按鈕:
隨後會出現單元測試窗口和覆蓋率窗口.
直接看覆蓋率窗口:
能夠看到該Controller和方法的覆蓋率都是100%了.
來到被測試的RootController裏:
Resharper(其實是dotCover) 在代碼的左邊顯示出了該行代碼是否已經被測試覆蓋, 若是都是綠色的就說明都被覆蓋了.
Resharper的代碼覆蓋率結果能夠導出多種格式:
例如導出HTML後也能夠查看覆蓋率明細:
這個ProductController略微複雜一點, 首先它須要不少依賴項.
看它的POST Action方法, 不少地方須要被測試:
首先能夠測試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%並不能說明代碼沒問題):
該項目使用的是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的測試就介紹這些吧.