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