使用Microsoft.AspNetCore.TestHost进行完整的功能测试
簡介
Microsoft.AspNetCore.TestHost是可以用于Asp.net Core 的功能測試工具。很多時候我們一個接口寫好了,單元測試什么的也都ok了,需要完整調試一下,檢查下單元測試未覆蓋到的代碼是否有bug。步驟為如下:程序打個斷點->F5運行->通常需要登錄個測試賬號->查找要調試api的入口->獲得斷點開始調試=>代碼報錯?很多時候需要停止調試修改->回到第一步。如此反復循環,做著重復的工作,Microsoft.AspNetCore.TestHost正是為了解決這個問題,它可以讓你使用xTest或者MSTest進行覆蓋整個HTTP請求生命周期的功能測試。
進行一個簡單的功能測試
新建一個Asp.net Core WebApi和xUnit項目
ValuesController里面自帶一個Action
我們在xUnit項目里面模擬訪問這個接口,首選安裝如下nuget包:
-
Microsoft.AspNetCore.TestHost
-
Microsoft.AspNetCore.All(很多依賴懶得找的話直接安裝這個集成包,百分之90涉及到AspNetCore的依賴都包含在里面)
然后需要引用被測試的AspnetCoreFunctionalTestDemo項目,新建一個測試類ValuesControllerTest
將GetValuesTest方法替換為如下代碼,其中startup類是應用自AspnetCoreFunctionalTestDemo項目
此時在ValueController打下斷點
?
運行GetValuesTest調試測試
成功進入斷點,我們不用啟動瀏覽器,就可以進行完整的接口功能測試了。
修改內容目錄與自動授權
上面演示了如何進行一個簡單的功能測試,但是存在兩個缺陷:
webApi在測試的時候實際的運行目錄是在FunctionalTest目錄下
對需要授權的接口不能正常測試,會得到未授權的返回結果
?1.內容目錄
我們可以在Controller的Get方法輸出當前的內容目錄
內容目錄是在測試x項目下這與我們的預期不符,如果webapi項目對根目錄下的文件有依賴關系例如appsetting.json則會找不到該文件,解決的辦法是在webHost中手動指定運行根目錄
[Fact]public void GetValuesTest() { ? ?var client = new TestServer(WebHost.CreateDefaultBuilder() ? ? ? ?.UseContentRoot(GetProjectPath("AspnetCoreFunctionalTestDemo.sln", "", typeof(Startup).Assembly)).UseStartup<Startup>()).CreateClient(); ? ?string result = client.GetStringAsync("api/values").Result;Assert.Equal(result, JsonConvert.SerializeObject(new string[] { "value1", "value2" })); }/// <summary>/// 獲取工程路徑/// </summary>/// <param name="slnName">解決方案文件名,例test.sln</param>/// <param name="solutionRelativePath">如果項目與解決方案文件不在一個目錄,例如src文件夾中,則傳src</param>/// <param name="startupAssembly">程序集</param>/// <returns></returns>private static string GetProjectPath(string slnName, string solutionRelativePath, Assembly startupAssembly) { ? ? ?string projectName = startupAssembly.GetName().Name; ? ? ?string applicationBasePath = PlatformServices.Default.Application.ApplicationBasePath; ? ? ?var directoryInfo = new DirectoryInfo(applicationBasePath); ? ? ?do{ ? ? ? ? ?var solutionFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, slnName)); ? ? ? ? ?if (solutionFileInfo.Exists){ ? ? ? ? ? ? ?return Path.GetFullPath(Path.Combine(directoryInfo.FullName, solutionRelativePath, projectName));}directoryInfo = directoryInfo.Parent;} ? ? ?while (directoryInfo.Parent != null); ? ? ?throw new Exception($"Solution root could not be located using application root {applicationBasePath}.");
}
?GetProjectPath方法采用遞歸的方式找到startup的項目所在路徑,此時我們再運行
2.自動授權
每次測試時手動登錄這是一件很煩人的事情,所以我們希望可以自動話,這里演示的時cookie方式的自動授權
首先在startup文件配置cookie認證
namespace AspnetCoreFunctionalTestDemo { ?? ??public class Startup{ ? ? ?
? ? ? ? ? ?public Startup(IConfiguration configuration){Configuration = configuration;} ? ?
? ? ? ?public IConfiguration Configuration { get; } ? ? ? ?// This method gets called by the runtime. Use this method to add services to the container.public void ConfigureServices(IServiceCollection services){services.AddMvc(); ? ? ? ? ?
??services.AddAuthentication(o => o.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(o =>{o.ExpireTimeSpan = new TimeSpan(0, 0, 30);o.Events.OnRedirectToLogin = (context) =>{context.Response.StatusCode = 401; ? ? ? ? ? ? ? ? ? ? ? return Task.CompletedTask;};}); ? ? ? ?} ? ? ? ?// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.public void Configure(IApplicationBuilder app, IHostingEnvironment env){ ? ? ? ? ? ?if (env.IsDevelopment()){app.UseDeveloperExceptionPage();} ? ? ? ? ? ?app.UseAuthentication();app.UseMvc();}} }
這里覆蓋了cookie認證失敗的默認操作改為返回401狀態碼。
在valuesController新增登錄的Action并配置Get的Action需要授權訪問
namespace AspnetCoreFunctionalTestDemo.Controllers {[Route("api/[controller]")] ??public class ValuesController : Controller{ ? ? ? ?
// GET api/values ? ? ?
? ? ? ?[HttpGet,Authorize] ? ?
? ? ? ?public IEnumerable<string> Get([FromServices]IHostingEnvironment env){ ? ? ? ? ? ?
return new string[] { "value1", "value2" };} ? ? ?
?// POST api/values[HttpGet("Login")] ?
? ? ?public void Login(){ ? ? ? ? ?
??var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);identity.AddClaim(new Claim(ClaimTypes.Name, "huanent")); ? ? ? ? ? ?var principal = new ClaimsPrincipal(identity);HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal).Wait();}} }
此時我們使用測試項目測試Get方法
如我們預期,返回了401,說明未授權。我們修改下GetValuesTest
?public class ValuesControllerTest{[Fact] ? ? ?
??public void GetValuesTest(){ ? ? ? ? ?
??var client = new TestServer(WebHost.CreateDefaultBuilder().UseStartup<Startup>().UseContentRoot(GetProjectPath("AspnetCoreFunctionalTestDemo.sln", "", typeof(Startup).Assembly))).CreateClient(); ? ? ? ?
? ?var respone = client.GetAsync("api/values/login").Result;SetCookie(client, respone); ? ? ? ? ? ?var result = client.GetAsync("api/values").Result;} ? ? ? ?private static void SetCookie(HttpClient client, HttpResponseMessage respone){ ? ? ? ? ? ?string cookieString = respone.Headers.GetValues("Set-Cookie").First(); ? ? ? ? ? ?string cookieBody = cookieString.Split(';').First();client.DefaultRequestHeaders.Add("Cookie", cookieBody);} ? ? ?
?/// <summary>/// 獲取工程路徑 ? ? ?
??/// </summary>/// <param name="slnName">解決方案文件名,例test.sln</param>/// <param name="solutionRelativePath">如果項目與解決方案文件不在一個目錄,例如src文件夾中,則傳src</param>/// <param name="startupAssembly">程序集</param>/// <returns></returns>private static string GetProjectPath(string slnName, string solutionRelativePath, Assembly startupAssembly){ ? ? ? ? ?
?string projectName = startupAssembly.GetName().Name; ? ? ? ? ?
??string applicationBasePath = PlatformServices.Default.Application.ApplicationBasePath; ? ? ? ?
? ?var directoryInfo = new DirectoryInfo(applicationBasePath); ? ? ? ?
? ?do{ ? ? ? ? ? ? ?
var solutionFileInfo = new FileInfo(Path.Combine(directoryInfo.FullName, slnName)); ? ? ? ? ? ?
?? ?if (solutionFileInfo.Exists){ ? ? ? ? ? ? ? ? ?
??return Path.GetFullPath(Path.Combine(directoryInfo.FullName, solutionRelativePath, projectName));}directoryInfo = directoryInfo.Parent;} ? ? ? ? ?
??while (directoryInfo.Parent != null); ? ? ?
? ? ?throw new Exception($"Solution root could not be located using application root {applicationBasePath}.");}} }
我們首先訪問api/Values/Login,獲取到Cookie,然后講cookie附在httpclient的默認http頭上,這樣就能夠成功訪問需要授權的接口了
總結
通過上面演示,我們已經可以很大程度地模擬了整個api請求,讓我們可以方便地一鍵調試目標接口,再也不用開瀏覽器或postman了。
附上演示項目地址:https://github.com/huanent/AspnetCoreFunctionalTestDemo
原文:http://www.cnblogs.com/huanent/p/7886282.html
.NET社區新聞,深度好文,歡迎訪問公眾號文章匯總 http://www.csharpkit.com
總結
以上是生活随笔為你收集整理的使用Microsoft.AspNetCore.TestHost进行完整的功能测试的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 可观测性与原生云监控
- 下一篇: Entity Framework Cor