本文須要您瞭解ASP.NET Core Web API 和 xUnit的相關知識.html
這裏有xUnit的介紹: http://www.javashuo.com/article/p-rqwpmfqu-gm.htmlweb
ASP.NET Core集成測試官方文檔: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1數據庫
測試金字塔, 但它只是一個指導性的概念.服務器
若是所單元測試是對一個組件進行隔離測試的話, 那麼集成測試則是測試多個組件共同協做產生出期待的結果.網絡
單元測試一般很快. 而集成測試則慢的多, 由於它須要不少配置, 而且可能依賴於外部的組件, 例如數據庫, 網絡, 文件等.函數
一般在一個項目裏單元測試要比集成測試多不少.單元測試
單元測試一般依賴於mock的組件, 而集成測試則使用可運行的組件.測試
注意: 若是一個行爲能夠經過單元測試或集成測試來測試的話, 那麼應該使用單元測試.優化
若是我想測試一個API Controller的Action, 我可能須要把這個項目運行起來, 等它跑起來, 發送請求並檢驗結果. 但這樣作的話須要不少的配置工做, 而且很麻煩.ui
幸虧ASP.NET Core 提供了一個Microsoft.AspNetCore.TestHost 庫, 使用它就無需單獨去運行被測試系統了.
ASP.NET Core應用裏, 咱們在Program.cs裏建立WebHostBuilder, 並配置Kestrel Web服務器, 使用Startup類進行應用配置, 註冊服務和中間件等. 最終在WebHostBuilder上使用Build()來建立WebHost的實例, 它能夠用來在特定的URL和端口上運行並監聽請求.
而這個TestHost庫也使用了WebHostBuilder, 但它會本身把構建和運行web宿主的工做處理好, 也就是建立出了一個TestServer. TestServer不會在網絡上進行監聽, TestServer建立了一個名爲Host的屬性, 它的類型是IWebHost, 它能夠用來處理內存裏的請求對象. TestServer還會暴露一個HttpClient, 你能夠用它來發送請求到被測試系統. 整個交互的過程都是在內存裏完成的.
下圖是被測試系統在生產環境和集成測試使用TestServer情形下的對比圖:
圖中:
當應用/被測試系統在生產環境運行的時候, 它使用Kestrel服務器, 監聽HTTP請求, 並把它轉化爲HttpContext, 而後再傳進ASP.NET Core的管道里.
TestServer不監聽網絡請求, 它使用HttpClient在內存裏發送請求.
仔細看一下集成測試時使用TestServer的流圖:
圖中能夠看到: 測試代碼建立TestServer, TestServer建立HttpClient. 測試代碼使用HttpClient發送請求接收響應. TestServer會轉化請求並交給ASP.NET Core MVC/API 應用來處理.
首先須要爲你的應用創建集成測試項目:
而後須要爲項目添加Microsoft.AspNetCore.TestHost 這個庫:
被測試的是這個Controller的GetRoot()所對應的行爲, 而不僅是這個方法:
測試返回NoContent:
這裏面按照以前講的順序, 建立IWebHostBuilder, 並用它建立TestServer, 而後TestServer建立HttpClient. 隨後就使用httpClient發送請求, 返回結果, Assert便可.
須要注意的是, 在建立IWebHostBuilder的時候, 我使用了被測試系統的Startup類來進行配置, 並設定的環境是Development.
因爲我這個項目能夠看做是真實項目, 因此第一次運行該測試的時候, 測試是Fail的. 由於Startup裏面有不少配置並不知足測試要求.
在我把IpRateLimiting, HttpsRedirection, Authentication, AuthorizeFilter等中間件/組件去掉以後, 測試才經過:
因此這就引出了一個問題, Startup裏面的配置在開發時 和 測試時 以及 生產運行時 多是不太同樣的.
個人Startup裏面已經有不少代碼了, 若是再進行環境判斷, 那就會更亂了.
因此我決定爲集成測試新創建一個Startup配置類:
ASP.NET Core項目也支持多環境的多個Startup配置類, 這部份內容請參考官方文檔: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-2.1#environment-based-startup-class-and-methods.
而後修改代碼, 使用這個測試專用的Startup便可:
測試會經過.
下面繼續測試GetRoot方法的另外一個路徑, 這個路徑會用到RootController的依賴項IUrlHelper.
在集成測試裏, 一般狀況下是不使用Mocking技術的. 因此在這裏我也不會mock IUrlHelper:
這裏沒有mock任何東西. 此外這個被測試的行爲須要設置AcceptHeader.
測試會Pass的, TestServer幫我搞定了一切:
寫了兩個測試方法, 又引出了一個新的問題: 這兩個方法有一些共同的設置代碼, 這些設置可能會比較耗資源. 咱們能夠把這些設置放在構造函數裏面, 可是若是使用Theory並含有多個InlineData的話, 就會屢次運行構造函數裏的設置代碼, 可能會很是好資源(時間).
因此咱們應該考慮使用test fixture 這裏有介紹: http://www.cnblogs.com/cgzl/p/8438019.html#share
並且咱們可使用WebApplicationFactory來構建TestServer, 使用WebApplicationFactory的好處是能夠靈活的進行自定義配置.
要使用WebApplicationFactory, 須要添加庫: Microsoft.AspNetCore.Mvc.Testing
使用該庫以後, 代碼應該以下:
可是卻有一個問題, 這裏我選擇的時StartupIntegrationTest. 而電腦環境變量設置的是Development, 而調試測試以後發現走的是StartupDevelopment.
也許這是個Bug? 或者就是這樣的意圖. 那我暫時仍是使用原始的方法建立TestServer吧, 下面是我使用的代碼:
創建一個TestServerFixture, 須要使用IDisposable來作清理工做:
而測試類注入該Fixture便可:
而後重跑測試, 會pass的:
我要測試這個Controller下CreateProduct方法對應的行爲. 該Controller須要不少依賴項, 其中兩個還須要使用數據庫.
一般狀況下集成測試裏使用的數據庫和生產環境中使用的數據庫不一樣, 在測試環境我更傾向於使用內存類數據庫.
EF Core裏面至少有兩個內存類的數據庫提供商:
在StartupIntegrationTest裏, 我就使用InMemory吧;
下面是測試方法的代碼:
這代碼其實很簡單, 就是對應着被測試的Controller方法作一些須要的設定便可, 例如Headers, Content-Type等等.
須要注意的是Content-Type是在Content的Header裏設置, 而不是Request的Headers裏設置, 不然會報亂用Header的錯.
該測試會pass:
最後針對該行爲再作一個Model驗證失敗的測試:
沒什麼不一樣, 就是model的Name屬性超長了.
這個測試一樣會經過:
集成測試就簡單介紹這些.......