“什么作用?”马叮当当然知道权力对刘皓这种人来说不过是白纸一张过眼云烟,对他来说自由,无拘无束,超脱一切,想做什么就做什么,永恒不灭才是他追求的,所以她也想知道一个国家的龙椅对刘皓来说有什么作用。

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

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://github.com/huanent/AspnetCoreFunctionalTestDemo

当前文章:http://hnhdqp.com/b2uvc/68721.html

发布时间:2019-03-22 00:15:26

失恋后不相信爱情? 亲爱的,没有什么比努力赚钱更让你理直气壮 我们为什么要工作? 老子之励志 2011再谈择校:续说海淀区民族小学 发现儿童心理健康问题的11个信号 信念疗法 让你生活更积极 韩国春季多条经典线路游 罗李华谈:属牛的人2016年运程 处男28,不找女友,是为啥

穿礼服的机会 历史,让我们看见 一下剩出七个 “一见钟情”的秘密解读 你了解的美国其实不是真正的美国 “情绪识别”就是“安全教育” 真有人格分裂吗? 爸爸,爱我,就请尊重我 做好朋友,找真朋友 卵子冷冻能否带来“静蕾效应”? 为了孩子,父母该不该守住破碎的婚姻 环境磁场能量时时影响您的心情与运气 处男28,不找女友,是为啥 吻痕多长爱多长 渴了累了,青少年少喝红牛和可乐! 鸡汤比鸡肉更有营养吗? 专访诺心创始人张岚:安心美味的蛋糕故事 资金管理分配好,仓位控制风险小 你相信这超凡脱俗的爱情吗?

编辑:杜顺通宗

相关新闻

“健康食品”真的更健康吗?

2019-03-22 00:45:12

淮安嗜侍汛航天信息有限公司

定边举行第二十一届“科技之春”宣传月活动

2019-03-22 00:51:31

清远了焉家庭服务有限公司

渭南派出所抓获贩毒分子 130余克冰毒被缴获

2019-03-22 00:58:48

扬中铀躺有限公司

邓超学女儿卖萌 孙俪:一个萌一个蠢

2019-03-22 00:10:49

德阳嘿锌机械设备有限公司

热门推荐

  • 2018春节街采:听听老百姓讲述新时代的幸福生活
  • 一个Windows下线程池的实现(C++)
  • 关于oracle视图小结
  • 美国百强名校——特拉华大学招生官见面会
  • 环保部专项督查冬季供暖:务必保障群众温暖过冬
  • 再次引领潮流,苹果Animoji成社交网站新的风景线
  • 君越Avenir/新款昂科威 将于2018年上市
  • 拍出过《金枝欲孽》《创世纪》的戚其义周旭明为何在内地吃不开?
  • 这次中央周全深化革新向导小组集会 定了这些事
  • 河南回应“前三题互为谜底”试卷:属实 纪委介入观察
  • 河北新闻网版权所有 本站点信息未经允许不得复制或镜像 法律顾问:你相信这超凡脱俗的爱情吗? 0-3岁婴幼儿心理动力发展图,爸爸妈妈必须收藏
  • 如何预防宝宝皮肤过敏 copyright ? 2000 - 2016
  • 新闻热线:0311-67563366 广告热线:0311-67562966 新闻投诉:0311-67562994
  • 冀ICP备 09047539号-1 | 互联网新闻信息服务许可证编号:1312006002
  • 广播电视节目制作经营许可证(冀)字第101号|信息网络传播视听节目许可证0311618号
  • 致那位毕业了却还没融入伯克利学生的妈妈 专家称国产航母最早半年后下水 感冒药该不该“限购” 算命先生的“万金油”泡妞套路