系列目录

通过前面两节讲解,我们的测试类中已经有两个测试方法了,总体上如下

 public class mvc20
{
private readonly HttpClient _client; public mvc20()
{
var builder = new WebHostBuilder()
.UseContentRoot(@"E:\personal project\newTest2018\ConsoleApp1\CoreMvc")
.UseEnvironment("Development")
.UseStartup<CoreMvc.Startup>();
var server = new TestServer(builder);
_client = server.CreateClient();
}
[Fact]
public async Task SimpleGet()
{
var response = await _client.GetAsync("/HelloWorld/Hello");
response.EnsureSuccessStatusCode();
var responseStr = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello,World", responseStr);
} [Theory]
[AutoData]
public async Task SimplePost(Student stud)
{
var content = new StringContent(JsonConvert.SerializeObject(stud), Encoding.UTF8, "application/json");
var response = await _client.PostAsync("/HelloWorld/StudentInfo", content);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
Assert.True(!string.IsNullOrEmpty(result));
}
}

改进一:将对象初始化移到外部类中

以上方法看似没有问题,实际上却有一个性能陷阱,我们通过前面章节的知识已经知道,xunit里测试类的构造函数会在每一个测试方法运行的时候都执行一遍,通常情况下我们的测试代码远不止三几个,有时候几十个甚至上百个.这样每次都创建一个是非常影响性能的.并且这里的TestServer和_client都没有释放.此外就是web项目里可能每一个测试类都需要创建这样一个TestServer,这样重复的代码会复制很多次,带来维护困难.

我们前面讲到过,我们如果想要让一个对象在一个测试类中只初始化一次,就要让这个类实现IClassFixture泛型接口,类在初始化的时候会自动注入这个泛型对象的实体,并且只初始化一次,如果这个泛型对象实现了IDisposable接口,则会在测试类所有方法都执行完成的时候执行这个对象里的Dispose方法.

首先我们创建一个名为MyTestServerFixtrue的类,TestServer和HttpClient对象的初始化在这里执行.代码如下

public class MyTestServerFixtrue:IDisposable
{
public readonly HttpClient _client;
private readonly TestServer _server;
public MyTestServerFixtrue()
{
var builder = new WebHostBuilder()
.UseContentRoot(@"E:\personal project\newTest2018\ConsoleApp1\CoreMvc")
.UseEnvironment("Development")
.UseStartup<CoreMvc.Startup>();
_server = new TestServer(builder);
_client = _server.CreateClient();
} public void Dispose()
{
_client.Dispose();
_server.Dispose();
}

这里的方法和参数大部分都和前面在测试类中添加的一样,只是有以下几点需要注意:

1.把server变量放在构造函数外边,这样我们才能在Dispose里把它释放掉,不然无法定位到它.

2.把client变成public类型,因为我们需要在测试类中访问它.

下面我们再看测试类改造后的代码

 public class mvc20:IClassFixture<MyTestServerFixtrue>
{
private readonly HttpClient _client; public mvc20(MyTestServerFixtrue fixtrue)
{
this._client = fixtrue._client;
}
}

这里是主要代码,首先这个实现了IClassFixture,然后我们把无参构造函数改变成有参的,并且传入MyTestServerFixtrue类型对象,Xunit会自动注入这个对象,然后我们把这个对象里的httpclient赋值给本类的_client对象,这样我们就可以在本类中使用它了.

这样其它的测试类也可以实现IClassFixture<MyTestServerFixtrue>,如果想要改TestServer的配置只需要在MyTestServerFixtrue类中改就行了.

改进二:固定路由参数

我们看到前面讲到的两个测试方法提交的路径中都包含"/HelloWorld",它其实匹配控制器名,一般情况下同一个Controller下的方法的测试方法都写在同一个测试类中.这样Controller名称是固定的,我们可以把它单独抽离出来,只需要Action后面的路由.

我们把测试类改成如下:

 public class mvc20:IClassFixture<MyTestServerFixtrue>
{
private readonly HttpClient _client; public mvc20(MyTestServerFixtrue fixtrue)
{
var baseAddr = fixtrue._client.BaseAddress.AbsoluteUri;
string controllerName ="HelloWorld";
this._client = fixtrue._client;
if (!fixtrue._client.BaseAddress.AbsoluteUri.Contains(controllerName))
{
fixtrue._client.BaseAddress = new Uri(baseAddr + controllerName+"/");
}
}
[Fact]
public async Task SimpleGet()
{
var response = await _client.GetAsync($"{nameof(HelloWorldController.Hello)}");
response.EnsureSuccessStatusCode();
var responseStr = await response.Content.ReadAsStringAsync();
Assert.Equal("Hello,World", responseStr);
} [Theory]
[AutoData]
public async Task SimplePost(Student stud)
{
var content = new StringContent(JsonConvert.SerializeObject(stud), Encoding.UTF8, "application/json");
var response = await _client.PostAsync($"{nameof(HelloWorldController.StudentInfo)}", content);
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
Assert.True(!string.IsNullOrEmpty(result));
}
}

这里我们把controller的名称加到HttpClient的BaseUrl里面,然后发送get,post等请求的时候只要Action的名字,这里我们使用nameof关键字来获取action的名字,使用nameof关键字来获取的好处是:第一,我们点击方法名就可以快速定位到指定的方法.更为重要的是如果方法的名称改了,编译的时候就会出现编译错误,我们可以快速定位到错误然后修改.

改进三:资源路径改为相对路径

上面MyTestServerFixtrue类中的代码有一处有明显问题:那就是UseContentRoot里的路径是写死的,项目在本机上地址与在服务器上的或者与其它同事的绝大多数情况下是不一样的(因为大家项目所在的目录名不相同)这时候如果其它人调用这些代码就可能会出现错误.

我们可以使用相对路径来获取绝对路来解决这个问题,由于这两个项目的主文件夹在同一文件夹下面,因此测试项目向外退若干层就能够得到mvc项目的主目录了.

我们将MyTestServerFixtrue类的构造方法改为如下:

public MyTestServerFixtrue()
{
var rootPath = GetContentRootDir();
var builder = new WebHostBuilder()
.UseContentRoot(rootPath)
.UseEnvironment("Development")
.UseStartup<CoreMvc.Startup>();
_server = new TestServer(builder);
_client = _server.CreateClient();
}

这次我们不是再写死rootPath而是通过方法GetContentRootDir来获取.

下面我们来看这个GetContentRootDir方法

 private string GetContentRootDir()
{
var currentPath = AppDomain.CurrentDomain.BaseDirectory;
var relativePath = @"..\..\..\..\CoreMvc";
var combinedPath = Path.Combine(currentPath, relativePath);
var absPath = Path.GetFullPath(combinedPath);
return absPath;
}

首先我们先获取当前程序域的目录,也就是程序的运行目录,获取到它之后我们看看向上移动多少层能够到达包含mvc项目和这个test项目的文件夹,经查是四层,下面的相对路径我们就写为如变量relativePath定义的那样.

我们把它们组合在一起,然后通过Path.GetFullPath来获取到相对路径的绝路径.

改进四 设置超时

有时候服务器故障会导致请求非常慢,服务器很长时间无法返回请求,这就会导致集成测试代码一直'卡'着无法完成,这时候可以设置一个超时.设置非常简单,HttpClient有一个Timeout属性,设置相应的超时时间即可.HttpClient的默认请求超时时间是100s,这个值应该大部分时候不需要修改的,但是关于具体的业务,可能有一些方法本身执行时间特别长(业务逻辑非常复杂,sql语句非常复杂等)这时候可以单元给本次请求设置一个超时时间.比如说是150s,设置如下

     CancellationTokenSource cts = new CancellationTokenSource(TimeSpan.FromSeconds(150));
var response = await client.GetAsync("/Home/index", cts.Token);

这里定义一个CancellationTokenSource对象,并指定超时时间,然后把此对象的Token对象传给异步请求方法.

.netcore持续集成测试篇之测试方法改造的更多相关文章

  1. .netcore持续集成测试篇之开篇简介及Xunit基本使用

    系列目录 为了支持跨平台,微软为.net平台提供了.net core test sdk,这样第三方测试框架诸如Nunit,Xunit等只需要按照sdk提供的api规范进行开发便可以被dotnet cl ...

  2. .netcore持续集成测试篇之搭建内存服务器进行集成测试一

    系列目录 在web项目里,我们把每一层的代码的单元测试都通过并不代表程序能正常运行,因为这个过程缺失了http管道,很多时候我们还还需要把项目布在iis环境中或者在vs里启动iis express服务 ...

  3. .netcore持续集成测试篇之Xunit结合netcore内存服务器发送post请求

    系列目录 Web项目中,很多与用户数据交互的请求都是Post请求,想必大家都用过HttpClient构造过post请求,这里并不对HttpClient做详细介绍,只介绍一些常用的功能.并结合AutoF ...

  4. .netcore持续集成测试篇之MVC测试

    前面我们讲的很多单元测试的的方法和技巧不论是在.net core和.net framework里面都是通用的,但是mvc项目里有一种比较特殊的类是Controller,首先Controller类的返回 ...

  5. .netcore持续集成测试篇之web项目验收测试

    系列目录 通过前面的单元测试,我们能够保证项目的基本模块功能逻辑是正常的,通过集成测试能够保证接口的请求是正常的.然而最终项目交付我们还需要对项目进行页面的行为进行测试,比如页面布局是否正常,按钮是否 ...

  6. .netcore持续集成测试篇之Xunit数据驱动测试一

    系列目录 Nunit里提供了丰富的数据测试功能,虽然Xunit里提供的比较少,但是也能满足很多场景下使用了,如果数据场景非常复杂,Nunit和Xunit都是无法胜任的,有不少测试者选择自己编写一个数据 ...

  7. .netcore持续集成测试篇之 .net core 2.1项目集成测试

    系列目录 从.net到.net core以后,微软非常努力,以每年一到两个大版本的频率在演进.net core,去年相继发布了.net core 2.1和2.2,其中2.1是长期支持版,不断的快速更新 ...

  8. .net持续集成测试篇之Nunit常见断言

    系列目录 Nunit测试基础之简单断言 在开始本篇之前需要补充一些内容,通过前面搭建Nunit测试环境我们知道要使一个方法成为单元测试方法首先要在此方法所在类加上TestFixture注解,并且在该方 ...

  9. .net持续集成测试篇之Nunit参数化测试

    系列目录 在进行单元测试的时候,很多时候,很多时候我们都是在单元测试方法内部提供特定的值,但是这样测试往往造成样本数不足从而导致覆盖的结果不够全面,很多时候我们更想提供来自外部的,满足条件的一组值来进 ...

随机推荐

  1. 《ASP.NET Core 高性能系列》致敬伟大的.NET斗士甲骨文!

    写在开始 三年前,曾写过一篇文章:从.NET和Java之争谈IT这个行业,当时遭到某些自认为懂得java就了不起的Javaer抨击, 现在可以致敬伟大的.NET斗士甲骨文了 (JDK8以上都需要收费, ...

  2. Linux命令学习-tail命令

    Linux中,tail命令的全称就是tail,主要用于监控日志文件. 对于一个正在运行应用来说,其对应的log日志文件肯定是在不断的更新,此时,便可通过tail命令来动态显示日志文件的内容.假设当前目 ...

  3. 【JavaScript】深入理解call,以及与apply、bind的区别

    一.call call有两个妙用 1.继承(我前面的文章有提到用call实现call继承,有兴趣可以看下.https://www.cnblogs.com/pengshengguang/p/105476 ...

  4. ElementUI 简要源码解析——Basic篇

    Layout 布局 row 布局组件中的父组件,用于控制子组件.很简单的一个布局标签,主要通过 justify 和 align 控制子元素的对齐方式,使用 render 函数通过传入的 tag 属性控 ...

  5. Spark 中 RDD的运行机制

    1. RDD 的设计与运行原理 Spark 的核心是建立在统一的抽象 RDD 之上,基于 RDD 的转换和行动操作使得 Spark 的各个组件可以无缝进行集成,从而在同一个应用程序中完成大数据计算任务 ...

  6. mplayer+ffmpeg 组合截图

    mplayer截图的优点:对于一个时长很长的视频,可以任意指定一个时间点截图,mplayer会直接跳到这个时间点开始解码截图: 缺点:由于是直接跳到指定的时间点,也就是直接跳过了之前的帧,这样解码出来 ...

  7. 从后端到前端之Vue(一)写个表格试试水

    目录: 1.脚本式开发. 2.工程化开发 3.工程化和脚本的区别 4.来个table试试水 4,1.目标 4.2.思路 4.3.设计与编码 4.4.效果 5.业务分离 6.功能拓展——个性化设置    ...

  8. Lucene01--倒排索引思想

    Lucene01--倒排索引思想 1. 倒排索引的概念: 首先对数据按列拆分存储,然后对文档中的数据分词,对词条进行索引,并记录词条在文档中出现的位置.这样查找时只要找到了词条,就找到了对应的文档.概 ...

  9. python 2.7 - 3.5 升级之路 (一) : 准备阶段开发环境 -- pip3, vitualEnv, pycharm

    背景 由于之前项目采用的版本是Python2.7, 考虑到Python 2.7 到明年(2020年后将会停止更新),以及为了更好的适应中文和拥抱新的特性.我们决定将其从python 2 升级到最新的 ...

  10. TensorFlow笔记-线程和队列

    线程和队列 在使用TensorFlow进行异步计算时,队列是一种强大的机制. 为了感受一下队列,让我们来看一个简单的例子.我们先创建一个“先入先出”的队列(FIFOQueue),并将其内部所有元素初始 ...