您的位置:首页 > 编程语言 > ASP

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

2017-11-29 00:00 316 查看


简介

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

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐