专栏首页用户6517667的专栏使用Microsoft.AspNetCore.TestHost进行完整的功能测试

使用Microsoft.AspNetCore.TestHost进行完整的功能测试

来源:http://www.51testing.com/

简介

  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调试测试

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

 修改内容目录与自动授权

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

  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认证

using System;  using System.Collections.Generic;  using System.Linq;  using System.Threading.Tasks;  using Microsoft.AspNetCore.Builder;  using Microsoft.AspNetCore.Hosting;  using Microsoft.Extensions.Configuration;  using Microsoft.Extensions.DependencyInjection;  using Microsoft.Extensions.Logging;  using Microsoft.Extensions.Options;  using Microsoft.AspNetCore.Authentication.Cookies;  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需要授权访问

using Microsoft.AspNetCore.Authentication;  using Microsoft.AspNetCore.Authentication.Cookies;  using Microsoft.AspNetCore.Authorization;  using Microsoft.AspNetCore.Hosting;  using Microsoft.AspNetCore.Mvc;  using System.Collections.Generic;  using System.Security.Claims;  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

using AspnetCoreFunctionalTestDemo;  using Microsoft.AspNetCore;  using Microsoft.AspNetCore.Hosting;  using Microsoft.AspNetCore.TestHost;  using Microsoft.Extensions.PlatformAbstractions;  using Newtonsoft.Json;  using System;  using System.Collections.Generic;  using System.IO;  using System.Linq;  using System.Net;  using System.Net.Http;  using System.Reflection;  using System.Text;  using System.Threading.Tasks;  using Xunit;  using static Microsoft.AspNetCore.WebSockets.Internal.Constants;  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://wap.ztestin.com/site/register?usercode=FAAAQwMQGAAXAwQBA3QhExcDHAQDPjVaABMIQg%3D%3D

本文分享自微信公众号 - 软件测试培训(iTestTrain),作者:软件测试培训

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2018-10-15

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • DevOps工具介绍连载(14)——裸金属服务器

    裸金属服务器,让传统物理机具有了自动发放、自动运维、VPC互联、支撑对接共享存储等云的能力。可以像虚拟机一样灵活的发放和使用,同时又具备了优秀的计算、存储、网络...

    小老鼠
  • Katalon Studio之接口测试中token处理

    1、新建一个API/Web Service的项目,在Object Repository中添加Web Service Request,method选择post,设...

    小老鼠
  • Jmeter通过参数传递多用户的并发测试

      2.添加csv data set config,filename为本地用户名存储记事本文件,file_encoding为utf-8(与记事本的编码格式一致)...

    小老鼠
  • 对话孟岩:通证经济背后的陷阱及方法论

    区块链大本营
  • c#:使用using关键字自动释放资源未必一定就会有明显好处

    记录这篇文章的灵感来源来自今天下班前与同事的小小争论,我现在开发的一个项目中,有这样一段代码: public string ToXML() { ...

    菩提树下的杨过
  • 计算流和控制流

    大家好,在上一次推送中,我们一起学习了Python数据结构中的逻辑值、字符串、列表和元组、集合set以及字典dict。今天我们一起来学习计算和控制流吧。

    陆勤_数据人网
  • Mac下使用iTerm2保存ssh登录密码方便无密登录

    mac下使用iTerm2,通过命令ssh需输入密码访问liunx,现在跟大家分享一个无密的方法。

    飞奔去旅行
  • 通过服务器端日志分析WebSocket的server和client交互

    测试过程:(每次测试只取第一次流程的记录) i.webshop不变化,拉取最新backend-ms代码部署 1.打开webshop 2.打开simulat...

    Jerry Wang
  • nodejs与前端js的区别

    很多前端程序员想玩nodejs开发,认为这是前端的一股趋势, 但真正能从前端js过渡到nodejs的却是凤毛麟角, 而看似和nodejs扯不上关系的后端程序员反...

    用户1608022
  • 「mysql优化专题」90%程序员都会忽略的增删改优化(2)

    通常情况下,当访问某张表的时候,读取者首先必须获取该表的锁,如果有写入操作到达,那么写入者一直等待读取者完成操作(查询开始之后就不能中断,因此允许读取者完成操作...

    java进阶架构师

扫码关注云+社区

领取腾讯云代金券