.netcore持續集成測試篇之搭建記憶體伺服器進行集成測試一
- 2019 年 10 月 3 日
- 筆記
在web項目里,我們把每一層的程式碼的單元測試都通過並不代表程式能正常運行,因為這個過程缺失了http管道,很多時候我們還還需要把項目布在iis環境中或者在vs里啟動iis express伺服器進行集成測試.對於返回view的的方法我們通常是在瀏覽器中輸入地址進行測試,對於返回數據的方法則使用諸如postman,fiddler,httpMaster pro,http debugger等工具進行測試.在這些工具中,postman相對好用,不但提供了測試數據的分組功能,還可以編寫腳本來完成一些自動化工作,比如自動添加固定http頭等.然而即便諸如postman這麼強大,依賴有很多不方便的地方.一旦方法過多,管理起來很不方便.並且構造複雜json或者formdata數據更是讓人心力絞悴,稍有一點格式錯誤就導致整個測試通不過,嚴重影響對錯誤點位置的判斷.更為重要的一點是,我們不論是在頁面中輸入地址,還是使用postman工具更多的是被動的去解決問題,也就是說等錯誤出現了以後我們才去測試相應的方法,很少見有誰使用postman把所有方法全部都跑一遍.
幸運的是.微軟在把.net升級到.net core之後,asp.net core 可以搭建一個記憶體伺服器來完成集成測試,這樣我們便可以不依賴於特定環境來測試我們的程式碼.這樣我們就在項目發布前可以先運行一下集成測試確保各方法都是可訪問的,減少項目上線後出現一些簡單錯誤的機率.
下面我們介紹如何搭建集成測試環境
產生我們使用VisualStudio自帶的模板創建一個Asp.net core web項目,在出現的對話框選項中選擇mvc(當然也可以不使用mvc項目,這裡不了演示方便起見).新建完mvc項目後,默認會有一個HomeController,想必大家都很熟悉了.此時我們再新建一個HelloWorld控制器,這個控制器目前只有一個Hello方法,程式碼如下
public class HelloWorldController : Controller { public IActionResult Hello() { return Content("Hello,World"); } }
然後我們使用VisualStudio自帶的Xunit模板創建一個測試項目(新建項目的時候切換到.net core標籤,裡面有Xunit測試項目模板)
此時,在測試項目里需要添加Microsoft.AspNetCore.TestHost Nuget包才可以進行集成測試.
需要注意的是,這個包要安裝在測試項目里,而不是core Mvc項目里
我們新建一個名為mvc20的測試類,程式碼如下
public class mvc20 { private readonly HttpClient _client; public mvc20() { var builder = new WebHostBuilder() .UseContentRoot(@"E:personal projectnewTest2018ConsoleApp1CoreMvc") .UseEnvironment("Development") .UseStartup<CoreMvc.Startup>(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task SimpleGet() { var response = await _client.GetAsync("/HelloWorld/Hello"); response.EnsureSuccessStatusCode(); var responseStr = await response.Content.ReadAsStringAsync(); Assert.Equal("Hello,World", responseStr); } }
配置詳解
下面我們來詳細分析這段程式碼
我們先來看構造函數里的程式碼,這裡的第一部分是創建一個WebHostBuilder,如果了解過.net core的同事可能對這段程式碼感到很熟悉,其實mvc項目也是通過WebHostBuilder創建一個webhost,我們打開剛創建的mvc項目,打開program入口類可以看到如下程式碼
public static void Main(string[] args) { BuildWebHost(args).Run(); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>() .Build();
可以看到BuildWebHost方法裡面使用了WebHost類來創建一個默認WebHostBuilder,這個方法裡面也有UseStartup方法,其實像親朋ContentRoot,UseEnvironment這些方法也可以在mvc項目里用,早期的.net core 項目模板生成的方法確實是這樣的,只是在.net core 2.0進行了更進一步的抽象,ContentRoot路徑,默認工作環境都按照慣例提供,不再需要手動指定.但是測試項目的工作環境和mvc項目的運行環境並不在同一個目錄,因此這裡需要手工指定.
UseContentRoot
此方法用於指定項目運行時資源文件的根目錄,通常情況下是創建項目的目錄,以上目錄大家根據創建項目的路徑來選擇,不要盲目拷貝以上程式碼.
UseEnvironment
此方法用於指定項目的運行環境,這裡我們指定為Development環境,這個環境是開發環境約定的默認環境,至於為什麼需要指定工作環境,因為很多時候開發環境和線上環境中使用到的組件是不一樣的,這在.net core項目里尤其明顯,比如說線上環境中我們使用的是正式的EntityFramework,而在工作環境下為了效率我們更傾向使用一個記憶體EntityFramework.還有一些工具類的框架只是在開發環境中使用的,線上環境並不需要它.
這個概念可能很容易理解,但是相信大家仍然看的一頭蒙圈,為什麼是Development,而不是Develop,debug呢,其實這個變數名是在mvc項目下的Properties裡面的launchSettings.json
里定義的.
因此如果我們提供的參數是"Development",mvc項目啟動的時候就把它和這個文件匹配,如果匹配成功則當前工作環境就是Development環境.
到於它是怎麼用,可以查看Startup.cs文件,裡面會注入IHostingEnvironment 對象實例,其中裡面的env.IsDevelopment就是通過這個值來判斷當前工作環境是否是Development環境,如果是工作環境,我們可以添加一些只有在工作環境中使用的程式碼.
UseStartup
此方法用於指定一個Startup文件,Startup文件在mvc5項目里也有,主要是關於中間件的配置,只是mvc5在啟動的時候自動調用一個名為Startup的文件(mvc5項目里這個文件不是必須的),而.net core里顯式指定一個startup文件,這樣就必須指定一個startup文件(名稱不必須是startup,可以是任意名,這裡是顯式指定的,因此程式中能找到). net core項目里startup文件必須指定否則整個項目就是一個普通操控台應該程式,沒法實現http功能,不論是mvc,webapi還是基本的http請求在.net core里都作為一個中間件,要使用必須配置.
回到測試方法里,這個startup文件直接指定為mvc項目里的startup文件即可,並且必須要這樣,如果測試環境中的startup文件和mvc項目里的不一樣測試就顯得沒有意義了.
TestServer
在測試環境里通過new方式創建一個TestServer,構造函數接收一個IWebHostBuilder類型的參數,我們把上面創建的WebHostBuilder傳入即可.
創建HttpClient
TestServer對象可以創建一個HttpClient對象,利用HttpClient對象我們便可以構建Http請求了.
需要說明的是這個HttpClient並沒什麼特殊的,它就是System.net.http下的httpclient,想必大家多少都用到過.
下面我們來看測試方法,測試方法主要就是通過HttpClient來發送Http請求,這裡的程式碼相信大家都並不陌生.
需要特別注意的是,這個測試方法和以往的不太相同,是一個async Task標識的非同步方法,之所以要使用非同步方法是因為HttpClient里的方法都是非同步的,如果嘗試使用同步的方法獲取結果,很容易造成死鎖.可能大家在工作中也確實使用過xxx.Result來同步阻塞獲取非同步結果,並且能正確返回真,但是阻塞HttpClient里的非同步方法很多時候都會造成死鎖.至於原因大家可以查看這篇文章
https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
小貼士,如何在Main方法裡面調用非同步函數,C# 7.1之前版本的Main方法是不支援非同步Main方法的,這給測試非同步方法帶來了很大的不便,c# 7.1開始支援非同步Main方法,我們可以把Main方法標識為Async Task Main(string[] args).然後我們在visual studio里對著項目右鍵點擊屬性,在Build標籤下選擇
高級
,彈出的對話框里語言版本下拉列表選擇c# 7.1或者更高(寫本文時,VisualStudio最高支援C# 7.3).這樣非同步Main方法便能編譯通過了.
下面言歸正傳,測試方法里的程式碼相必大家都很熟悉了,這裡有一點可能有些同事沒有遇到過那就是EnsureSuccessStatusCode
從字面上看它的意思是保證成功狀態,實際上它並不是保證請求一定能夠返回成功,而是返回的狀態碼不是200的時候就拋出一個異常.
需要注意的是
EnsureSuccessStatusCode
請求返回的必須是200才能通過,201,204這樣的狀態碼也會拋出異常.大家在開發過程中一定要根據實際情況來決定是否使用它.一般情況下前後端分離的項目里很多項目組都對http請求返回的結果進行了封閉,不管成功或者失敗都返回200,具體成功失敗是通過一個額外的其它欄位來確實的.這種情況下需要使用EnsureSuccessStatusCode,如果有對外介面很多時候會返回201,204,304,403等http狀態,這時候如果 使EnsureSuccessStatusCode則結果不是我們期待的.