使用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项目


        [Fact]        public void GetValuesTest(){            var client = new TestServer(WebHost.CreateDefaultBuilder().UseStartup<Startup>()).CreateClient();            string result = client.GetStringAsync("api/values").Result;Assert.Equal(result, JsonConvert.SerializeObject(new string[] { "value1", "value2" }));}


此时在ValueController打下断点

 

运行GetValuesTest调试测试

成功进入断点,我们不用启动浏览器,就可以进行完整的接口功能测试了。

修改内容目录与自动授权

上面演示了如何进行一个简单的功能测试,但是存在两个缺陷:

  1. webApi在测试的时候实际的运行目录是在FunctionalTest目录下

  2. 对需要授权的接口不能正常测试,会得到未授权的返回结果

 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


namespace FunctionalTest
{   
 
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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/322768.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MockJs案例

有时候前端写好模板后&#xff0c;后端还完工&#xff0c;那么总不能一直让项目停滞吧&#xff0c;这里就用Mockjs来模拟后端接口的数据&#xff0c;让我们先人一步完成项目。 首先创建一个html&#xff0c;导入axios和mockjs 再用mock去拦截请求&#xff0c;如果后端接口写好了…

Entity Framework Core 使用HiLo生成主键

HiLo是在NHibernate中生成主键的一种方式&#xff0c;不过现在我们可以在Entity Framework Core中使用。所以在这篇内容中&#xff0c;我将向您在介绍如何在Entity Framework Core中使用HiLo生成主键。 什么是Hilo&#xff1f; HiLo是High Low的简写&#xff0c;翻译成中文叫高…

Echarts报错:Component series.lines not exists. Load it first.

前几天用的echarts标签是bootcdn的 <script src"https://cdn.bootcdn.net/ajax/libs/echarts/4.7.0/echarts-en.common.js"></script>用着官方给的案例还可以&#xff0c;但是一用gallery社区里面的例子就报错 后来经过不断调试终于知道是需要换个cdn&…

认识微软Visual Studio Tools for AI

微软已经发布了其 Visual Studio Tools for AI 的测试版本&#xff0c;这是微软 Visual Studio 2017 IDE 的扩展&#xff0c;可以让开发人员和数据科学家将深度学习模型嵌入到应用程序中。Visual Studio Tools for AI 工具同时支持 Microsoft 的 Cognitive Toolkit 和 Google 的…

mybatis源码阅读(一):SqlSession和SqlSessionFactory

转载自 mybatis源码阅读(一)&#xff1a;SqlSession和SqlSessionFactory 一、接口定义 听名字就知道这里使用了工厂方法模式&#xff0c;SqlSessionFactory负责创建SqlSession对象。其中开发人员最常用的就是DefaultSqlSession &#xff08;1&#xff09;SqlSession接口定义…

开源纯C#工控网关+组态软件(六)图元组件

一、 图元概述 图元是构成人机界面的基本单元。如一个个的电机、设备、数据显示、仪表盘&#xff0c;都是图元。构建人机界面的过程就是铺排、挪移、定位图元的过程。 图元设计是绘图和编码的结合。因为图元不仅有显示和动画&#xff0c;还有背后操纵动画的控制逻辑。 一个好…

git合并分支的策略(赞)

假设当前有两个分支 master和test&#xff0c;两个分支一模一样&#xff0c;都有这三个文件 现在test添加一个test4.txt&#xff0c;然后提交到本地&#xff08;git add . git commit&#xff09; 切换到master分支上&#xff0c;git checkout master git merge test 这样mase…

改造独立部署(SCD)模式下.NET Core应用程序 dotnet的exe文件启动过程

设置一个小目标 改造前 改造后 独立部署SCD模式&#xff0c;是指在使用dotnet publish 命令时带上-r 参数运行时标识符&#xff08;RID&#xff09;。 目标提出原因&#xff1a;SCD模式下文件太乱了&#xff0c;很多文件在开发时大多又涉及不到&#xff0c;发布后如果能把文件…

mybatis源码阅读(四):mapper(dao)实例化

转载自 mybatis源码阅读(四)&#xff1a;mapper(dao)实例化 在开始分析之前&#xff0c;先来了解一下这个模块中的核心组件之间的关系&#xff0c;如图&#xff1a; 1.MapperRegistry&MapperProxyFactory MapperRegistry是Mapper接口及其对应的代理对象工程的注册中心&…

自定义路由匹配和生成

前言 前两篇文章主要总结了CMS系统两个技术点在ASP.NET Core中的应用&#xff1a; 《ASP.NET Core 中的SEO优化&#xff08;1&#xff09;&#xff1a;中间件实现服务端静态化缓存》 《ASP.NET Core 中的SEO优化&#xff08;2&#xff09;&#xff1a;中间件中渲染Razor视图》…

如何封装并发布一个属于自己的ui组件库

以前就一直有个想法自己能不能封装一个类似于elementui一样的组件库&#xff0c;然后发布到npm上去&#xff0c;毕竟前端说白了&#xff0c;将组件v上去&#xff0c;然后进行数据交互。借助这次端午&#xff0c;终于有机会&#xff0c;尝试自己去封装发布组件库了 我这里了只做…

听云支持.NET Core的应用性能监控

随着微软于2017年8月正式发布.NET Core 2.0&#xff0c; .NET Core 社区开始活跃&#xff0c;众多.NET开发者开始向跨平台转变。 听云于2017年11月推出了.NET Core应用监控工具&#xff0c;和听云其他语言的监控工具一样&#xff0c;.NET Core应用监控工具具有以下特征&#xf…

mybatis源码阅读(五) ---执行器Executor

转载自 mybatis源码阅读(五) ---执行器Executor 1. Executor接口设计与类结构图 public interface Executor {ResultHandler NO_RESULT_HANDLER null;// 执行update&#xff0c;delete&#xff0c;insert三种类型的sql语句int update(MappedStatement ms, Object parameter…

[52ABP实战系列] .NET CORE实战入门第三章更新了

早安 各位道友好&#xff0c;.NET CORE入门视频的第三章也算录制完毕了。欢迎大家上传课网进行学习。 更新速度 大家也知道最近的社会新闻比较多。频繁发生404、关键字打不出来&#xff0c;我个人也在关注这些事件。导致精力分散&#xff0c;没有做到稳定更新&#xff0c;现在呢…

如何安装nuxt

因为vue是单页面应用&#xff0c;所以不被Seo&#xff0c;如百度和Google抓取到&#xff0c;在Vue中如果想要爬虫爬到就必须使用nuxt 那么如何安装使用呢&#xff1f; yarn create nuxt-app <project-name> cd <project-name> yarn build yarn start必须先build&a…

mybatis源码阅读(六) ---StatementHandler了解一下

转载自 mybatis源码阅读(六) ---StatementHandler了解一下 StatementHandler类结构图与接口设计 BaseStatementHandler&#xff1a;一个抽象类&#xff0c;只是实现了一些不涉及具体操作的方法 RoutingStatementHandler&#xff1a;类似路由器&#xff0c;根据配置文件来路由…

基于OIDC(OpenID Connect)的SSO(纯JS客户端)

在上一篇基于OIDC的SSO的中涉及到了4个Web站点&#xff1a; oidc-server.dev&#xff1a;利用oidc实现的统一认证和授权中心&#xff0c;SSO站点。 oidc-client-hybrid.dev&#xff1a;oidc的一个客户端&#xff0c;采用hybrid模式。 oidc-client-implicit.dev&#xff1a;od…

IIS中的 Asp.Net Core 和 dotnet watch

在基于传统的.NET Framework的Asp.Net Mvc的时候&#xff0c;本地开发环境中可以在IIS中建立一个站点&#xff0c;可以直接把站点的目录指向asp.net mvc的项目的根目录。然后build一下就可以在浏览器里面刷新到最新的修改了&#xff0c;也可以附加到w3wp的进程进行调试。但是在…

foreach方法使用

用法 foreach方法主要是针对数组而言的&#xff0c;对数组中的每个元素可以执行一次方法 var array [a, b, c, e]; array.forEach((a)> {console.log(a); });属性 foreach方法主要有三个参数&#xff0c;分别是数组内容、数组索引、整个数组 var array [a, b, c, e]; arra…

.NET Core 控制台程序读 appsettings.json 、注依赖、配日志、设 IOptions

.NET Core 控制台程序没有 ASP.NET Core 的 IWebHostBuilder 与 Startup.cs &#xff0c;那要读 appsettings.json、注依赖、配日志、设 IOptions 该怎么办呢&#xff1f;因为这些操作与 ASP.NET Core 无依赖&#xff0c;所以可以自己动手&#xff0c;轻松搞定。 1、读 appsett…