Integration testing
Integration testing
集成测试用来确保app的不同模块之间可以正确的一起工作。ASP.NET Core提供单元测试框架和内建的测试网络服务来支持集成测试,并且测试网络服务不需要网络开销就可以处理请求。
Introduction to integration testing
不像单元测试,集成测试通常包含app的基础设施问题,比如,数据库,文件系统,网络资源,网络请求和响应。单元测试使用假的或模拟的对象来代替这些问题,但是集成测试的目的是确认系统在这些基础设施之上是否能正常的运行。
集成测试,运行更大量的代码段,并且因为他们依赖于基础设施元素,往往比单元测试慢几个数量级。因此,限制编写的集成测试数量是个好主意,尤其是当你可以使用单元测试来测试相同的行为。
Note
如果一些行为可以使用单元测试或集成测试来测试,请使用单元测试,因为它几乎总是更快。 您可能有许多或几百个单元测试与许多不同的输入,但只是一些覆盖最重要的场景的集成测试。
不需要写太多的集成测试来确认你能够写入和读取数据到数据库。不需要列举每一个可能的组合,只需要确认app可以一起正常工作就可以。
Integration testing ASP.NET Core
要设置好集成测试并运行,需要创建一个测试项目,添加需要测试的项目的ASP.NET Core网站应用,并安装一个测试运行器。这个过程在Unit testing有描述。
Note
将单元测试和集成测试分成不同的项目,将帮助你避免意外的将基础架构问题引入单元测试,并且可以很容易的选择启动哪一组测试。
The Test Host
ASP.NET Core包含一个测试主机,可以添加到集成测试项目,并用于托管ASP.NET Core应用程序,提供测试请求,而不需要真正的Web主机。
所提供的示例包括已配置为使用xUnit和测试主机的集成测试项目。 它使用Microsoft.AspNetCore.TestHost NuGet包。
一旦Microsoft.AspNetCore.TestHost包包含在项目中,您就可以在测试中创建和配置TestServer。 以下测试显示如何验证对网站根目录发出的请求返回“Hello World!”。 并应成功运行由Visual Studio创建的ASP.NET Core的默认空Web模板。
public class PrimeWebDefaultRequestShould
{
private readonly TestServer _server;
private readonly HttpClient _client;
public PrimeWebDefaultRequestShould()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
}
[Fact]
public async Task ReturnHelloWorld()
{
// Act
var response = await _client.GetAsync("/");
response.EnsureSuccessStatusCode();
var responseString = await response.Content.ReadAsStringAsync();
// Assert
Assert.Equal("Hello World!",
responseString);
}
}
此测试使用Arrange-Act-Assert模式。 Arrange步骤在构造函数中完成,它创建一个TestServer的实例。 一个配置好的WebHostBuilder将用于创建TestHost; 在此示例中,来自被测系统(SUT)的Startup类的Configure方法被传递给WebHostBuilder。 此方法将用于配置TestServer的请求管道,与SUT服务器的配置方式相同。
在测试的Act部分,向TestServer实例发出“/”路径的请求,并将响应读回到字符串中。 此字符串与预期的“Hello World!”字符串进行比较。 如果它们匹配,则测试通过; 否则失败。
现在,您可以添加一些额外的集成测试,以确认素数检查功能能在app中正常运行。
public class PrimeWebCheckPrimeShould
{
private readonly TestServer _server;
private readonly HttpClient _client;
public PrimeWebCheckPrimeShould()
{
// Arrange
_server = new TestServer(new WebHostBuilder()
.UseStartup<Startup>());
_client = _server.CreateClient();
}
private async Task<string> GetCheckPrimeResponseString(
string querystring = "")
{
var request = "/checkprime";
if(!string.IsNullOrEmpty(querystring))
{
request += "?" + querystring;
}
var response = await _client.GetAsync(request);
response.EnsureSuccessStatusCode();
return await response.Content.ReadAsStringAsync();
}
[Fact]
public async Task ReturnInstructionsGivenEmptyQueryString()
{
// Act
var responseString = await GetCheckPrimeResponseString();
// Assert
Assert.Equal("Pass in a number to check in the form /checkprime?5",
responseString);
}
[Fact]
public async Task ReturnPrimeGiven5()
{
// Act
var responseString = await GetCheckPrimeResponseString("5");
// Assert
Assert.Equal("5 is prime!",
responseString);
}
[Fact]
public async Task ReturnNotPrimeGiven6()
{
// Act
var responseString = await GetCheckPrimeResponseString("6");
// Assert
Assert.Equal("6 is NOT prime!",
responseString);
}
}
注意!!!上面的集成测试不是用来检查素数检查器的正确性的,而是检查app是否像你期望的那样运行,因为素数检查器的正确性是单元测试覆盖的范围。
Refactoring to use middleware
重构是改变应用程序代码以改进其设计而不改变其行为的过程。 理想情况下,有一套已经通过的测试,可以帮助确保系统的行为在更改之前和之后保持不变。 查看在Web应用程序的Configure方法中实现主要检查逻辑的方式,您将看到:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.Run(async (context) =>
{
if (context.Request.Path.Value.Contains("checkprime"))
{
int numberToCheck;
try
{
numberToCheck = int.Parse(context.Request.QueryString.Value.Replace("?", ""));
var primeService = new PrimeService();
if (primeService.IsPrime(numberToCheck))
{
await context.Response.WriteAsync($"{numberToCheck} is prime!");
}
else
{
await context.Response.WriteAsync($"{numberToCheck} is NOT prime!");
}
}
catch
{
await context.Response.WriteAsync("Pass in a number to check in the form /checkprime?5");
}
}
else
{
await context.Response.WriteAsync("Hello World!");
}
});
}
这个代码可以正常运行,但它远不是你想要实现的那样,即使这么简单。 想象一下,如果你需要在每次添加另一个URL路径时都添加这么多的代码,Configure方法会是什么样子?
一个选项是考虑在app中添加MVC,创建一个控制器来处理素数的检查。然而,假设你现在不需要MVC的其他功能,因为它太大了。
可以利用ASP.NET Core middleware,它可以让我们再自己的类中封装素数检查逻辑,并让Configure方法更好的分离关注点。
您希望允许中间件使用路径作为参数,因此中间件类在其构造函数中需要一个RequestDelegate和一个PrimeCheckerOptions实例。 如果请求的路径与此中间件配置的不匹配,您只需调用链中的下一个中间件,不做任何进一步操作。 配置中的其余实现代码现在位于Invoke方法中。
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using PrimeWeb.Services;
using System;
using System.Threading.Tasks;
namespace PrimeWeb.Middleware
{
public class PrimeCheckerMiddleware
{
private readonly RequestDelegate _next;
private readonly PrimeCheckerOptions _options;
private readonly PrimeService _primeService;
public PrimeCheckerMiddleware(RequestDelegate next,
PrimeCheckerOptions options,
PrimeService primeService)
{
if (next == null)
{
throw new ArgumentNullException(nameof(next));
}
if (options == null)
{
throw new ArgumentNullException(nameof(options));
}
if (primeService == null)
{
throw new ArgumentNullException(nameof(primeService));
}
_next = next;
_options = options;
_primeService = primeService;
}
public async Task Invoke(HttpContext context)
{
var request = context.Request;
if (!request.Path.HasValue ||
request.Path != _options.Path)
{
await _next.Invoke(context);
}
else
{
int numberToCheck;
if (int.TryParse(request.QueryString.Value.Replace("?", ""), out numberToCheck))
{
if (_primeService.IsPrime(numberToCheck))
{
await context.Response.WriteAsync($"{numberToCheck} is prime!");
}
else
{
await context.Response.WriteAsync($"{numberToCheck} is NOT prime!");
}
}
else
{
await context.Response.WriteAsync($"Pass in a number to check in the form {_options.Path}?5");
}
}
}
}
}
由于此中间件在其路径匹配时充当请求委派链中的端点,因此当此中间件处理请求时,不会调用_next.Invoke。
创建好了这个中间件和一些有用的扩展方法,配置他们更容易,重构的配置方法如下所示:
public void Configure(IApplicationBuilder app,
IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UsePrimeChecker();
app.Run(async (context) =>
{
await context.Response.WriteAsync("Hello World!");
});
}
在重构之后,可以确信Web应用程序仍然像以前一样工作,因为您的集成测试全部通过。
Note
在完成重构和测试通过后,最好将更改提交给源代码控制服务。 如果你正在练习测试驱动开发,考虑添加Commit到你的Red-Green-Refactor循环,详见这里
Resources
Integration testing的更多相关文章
- 系统内部集成测试(System Integration Testing) SIT 用户验收测试(User Acceptance Testing)
系统内部集成测试(System Integration Testing) SIT 用户验收测试(User Acceptance Testing) UAT SIT在前,UAT在后,UAT测完才可以上线
- Unit Testing, Integration Testing and Functional Testing
转载自:https://codeutopia.net/blog/2015/04/11/what-are-unit-testing-integration-testing-and-functional- ...
- 谈谈集成测试(integration testing)
对于软件开发来说,软件测试是一个几乎贯穿所有阶段的活动,所以测试的重要性毋庸置疑.不同开发组织如何在不同的产品研发阶段进行测试,也在很大程度上反映了其研发能力和质量控制能力.软件测试有很多类型,包括单 ...
- 学习笔记之Unit testing/Integration testing/dotnet test and xUnit
source code https://github.com/haotang923/dotnet/tree/master/src Unit testing C# code in .NET Core u ...
- Rails 5 Test Prescriptions 第8章 Integration Testing with Capybara and Cucumber
Capybara: A complete reference is available atrubydoc.info. 集成测试就是把局部的程序组合起来测试. 端到端测试是一个特殊的集成测试,覆盖了 ...
- Rails 5 Test Prescriptions 第9章 Testing-JavaScript: Integration Testing,❌挂一个问题webpacker::helper
使用Capybara进行JS的集成测试 谈论驱动 让测试通过 Webpack in Development Mode Js设计 是用户在网页上有好的体验的重要因素. 尽管如此,许多网页不测试JS. 部 ...
- 玩转单元测试之Testing Spring MVC Controllers
玩转单元测试之 Testing Spring MVC Controllers 转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html The Spri ...
- 单元测试unit test,集成测试integration test和功能测试functional test的区别
以下内容转自 https://codeutopia.net/blog/2015/04/11/what-are-unit-testing-integration-testing-and-function ...
- 40. Testing Prev Part IV. Spring Boot features
40. Testing Spring Boot provides a number of utilities and annotations to help when testing your app ...
随机推荐
- CentOS7的systemctl使用
CentOS 7开始,CentOS开始使用systemd服务来代替daemon,原来管理系统启动和管理系统服务的相关命令全部由systemctl命令来代替. 1.原来的 service 命令与 sys ...
- 日志切割logrotate和定时任务crontab详解
1.关于日志切割 日志文件包含了关于系统中发生的事件的有用信息,在排障过程中或者系统性能分析时经常被用到.对于忙碌的服务器,日志文件大小会增长极快,服务器会很快消耗磁盘空间,这成了个问题.除此之外,处 ...
- Vue源码探究-事件系统
Vue源码探究-事件系统 本篇代码位于vue/src/core/instance/events.js 紧跟着生命周期之后的就是继续初始化事件相关的属性和方法.整个事件系统的代码相对其他模块来说非常简短 ...
- jQuery 淡入淡出有png图的时候 ie8下有黑色边框
jQuery fadeTo 时ie8 png图片有黑色边框 往带有png图的样式里加 filter:progid:DXImageTransform.Microsoft.AlphaImageLoader ...
- 【Linux命令】nohup和&差异,查看进程和终止进程!
最近在开发dueros的技能,官方提供的PHPSDK中有多个实力,而运行实例的命令如下是 nohup php -S 0.0.0.0:8029 myindex.php & 从命令来看,肯定是在8 ...
- 【mysql】【转发】Cannot proceed because system tables used by Event Scheduler were found damaged at server start
本地:mac 10.12.3 mysql 5.6 远程:linux 7.3 mysql 5.7.18. (远程数据库yum安装,又5.6升级到5.7) 步骤:从本地数据库导出数据到远 ...
- 【laravel】【转发】laravel 导入导出excel文档
1.简介 Laravel Excel 在 Laravel 5 中集成 PHPOffice 套件中的 PHPExcel ,从而方便我们以优雅的.富有表现力的代码实现Excel/CSV文件的导入和 导出 ...
- 这五本Python急速入门必读的书,送给正在学习Python的你!
书籍是人类进步的阶梯,这句话从古至今都是适用的.为什么会这么说呢?书籍,它记录了人们实践的经验,这些经验有助于我们快速的学习,对于编程学习来说也不例外,今天就给大家带来了以下的书籍干货,希望能够帮助到 ...
- POJ:1185-炮兵阵地(状压dp入门)
炮兵阵地 Time Limit: 2000MS Memory Limit: 65536K Description 司令部的将军们打算在N*M的网格地图上部署他们的炮兵部队.一个N*M的地图由N行M列组 ...
- [python学习篇][书籍学习][python standrad library][内建函数]之[all,any,basestring,isinstance,bin,bool,@classmethod,@staticmethod,cmp,enumerate
Python 解释器内置了一些函数,它们总是可用的.这里将它们按字母表顺序列出. Built-in Functions abs() divmod() input() open() st ...