简介

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 

使用Microsoft.AspNetCore.TestHost进行完整的功能测试相关推荐

  1. ASP.NET Core 源码阅读笔记(5) ---Microsoft.AspNetCore.Routing路由

    这篇随笔讲讲路由功能,主要内容在项目Microsoft.AspNetCore.Routing中,可以在GitHub上找到,Routing项目地址. 路由功能是大家都很熟悉的功能,使用起来也十分简单,从 ...

  2. CS0579 Duplicate 'Microsoft.AspNetCore.Mvc.ApplicationParts.ProvideApplicationPartFactoryAttribute

    是由于系统中存在 两个不同版本的 Microsoft.AspNetCore.Mvc

  3. 剖析 Microsoft.AspNetCore.Identity 的精髓 ——菜鸟入门

    学习目录 前言 初级 菜鸟入门 配置详解 简单二次开发 进阶 UserManager 解析 RoleManager 解析 Validator 解析 SignInManager 解析 关于如何使用 博客 ...

  4. Microsoft.AspNetCore.Server.Kestrel.Core.BadHttpRequestException: Request body too large

    .ConfigureWebHostDefaults(webBuilder =>{webBuilder.UseUrls("http://localhost:6660");web ...

  5. 如何测试ASP.NET Core Web API

    在本文中,我们将研究如何测试你的ASP .NET Core 2.0 Web API解决方案.我们将了解使用单元测试进行内部测试,使用全新的ASP .NET Core的集成测试框架来进行外部测试. 本文 ...

  6. 《ASP.NET Core 微服务实战》-- 读书笔记(第3章)

    第 3 章 使用 ASP.NET Core 开发微服务 微服务定义 微服务是一个支持特定业务场景的独立部署单元.它借助语义化版本管理.定义良好的 API 与其他后端服务交互.它的天然特点就是严格遵守单 ...

  7. 如何测试 ASP.NET Core Web API

    在本文中,我们将研究如何测试你的 ASP .NET Core 2.0 Web API 解决方案.我们将了解使用单元测试进行内部测试,使用全新的 ASP .NET Core 的集成测试框架来进行外部测试 ...

  8. 配置实体框架DbContext的可扩展方案

    介绍 在ASP.NET Core Web应用程序中配置DbContext时,通常使用如下扩展方法:AddDbContext services.AddDbContext<xxxDbContext& ...

  9. 在ASP.NET Core 2.0中创建Web API

    目录 介绍 先决条件 软件 技能 使用代码 第01步 - 创建项目 第02步 - 安装Nuget包 步骤03 - 添加模型 步骤04 - 添加控制器 步骤05 - 设置依赖注入 步骤06 - 运行We ...

最新文章

  1. HTTP头入门到精通(每一个HTTP消息头解释)
  2. 《C#精彩实例教程》小组阅读07 -- C#字符与字符串
  3. mysql查看数据文件ibdata_如何从 ibdata文件 恢复 MySQL 数据库
  4. 创建第二个 local network - 每天5分钟玩转 OpenStack(84)
  5. 第三十章 elk(1) - 第一种架构(最简架构)
  6. 无符号定点数加法运算的VHDL描述
  7. java多次点击时事件_click事件的累加绑定,绑定一次点击事件,执行多次
  8. Cisco ACS AAA服务器导入华为私有属性
  9. LeetCode(804)——唯一摩尔斯密码词(JavaScript)
  10. 【信息系统项目管理师】第4章-项目整体管理 知识点详细整理
  11. gitlab 删除分支_idea gitlab 分支 pull、push 实践笔记
  12. javascript循环事件只响应最后一次的问题处理
  13. Deploy Apache Flink Natively on YARN/Kubernetes
  14. 计算机文献检索过程,计算机文献检索的基本方法与策略
  15. Unity3D手机游戏开发
  16. CNN之Xception Keras实现模型训练
  17. 去面试却被问的哑口无言,是不是踏入了机器学习误区
  18. 电脑饥荒服务器未响应,《饥荒》联机版常见问题及解决方法一览
  19. Windows 事件日志分析管理
  20. 使用eclipse和JavaFX Scene Builder进行快速构建JavaFX应用程序

热门文章

  1. Linux命令大总结(早期学习时的笔记)
  2. MongoDB 权限认证
  3. Android数据库Realm实践
  4. 链表笔试题汇编(一)
  5. windows下整合tomcat和nginx
  6. C# Barrier类
  7. C# 数据适配器之 DataAdapter 对象
  8. 互联网巨头基于全球产业链打造ARM CPU
  9. 浅谈.Net异步编程的前世今生----异步函数篇(完结)
  10. Id都是“とつくとき”这样的怎么爬,在线等,急