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 ...
随机推荐
- PHP计算今天、昨天、本周、本月、上月开始时间和结束时间
PHP计算今天.昨天.本周.本月.上月开始时间和结束时间 $today = date('Y-m-d H:i:s',mktime(0,0,0,date('m'),date('d'),date('Y')) ...
- Vim编辑器基础
Vim编辑器基础 vi:Visual Interface vim:VI iMproved Vim模式 1.编辑模式(命令模式) 只能下达命令,不能键入字符 2.输入模式 键入字符 3.末行模式 左下角 ...
- Linux菜鸟起飞之路【八】文本编辑器
在Linux中,文本编辑器有两个,VI和VIM.这两个编辑器用法差不多,但vim是vi的升级版,所以功能更强大一些. vim编辑器一共有三种模式,命令行模式.编辑模式和扩展模式. 进入vim界面,首先 ...
- 使用el-checkbox实现全选,点击失效没有反应
最近在公司接收到了一个需求,给收藏夹的书籍添加批量.全选删除实现思路:点击全选改变item的checked,改变item的checked,重新便利一下所有item的checked来改变全选的selec ...
- 【LeetCode】Linked List Cycle II(环形链表 II)
这是LeetCode里的第142道题. 题目要求: 给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 说明:不允许修改给定的链表. 进阶:你是否可以不用额外空间解决此题? ...
- JSP 页面 jstl 时间戳 long型转时间
转载http://www.cnblogs.com/gmq-sh/p/5528989.html
- [转] Vuex入门(2)—— state,mapState,...mapState对象展开符详解
1.state state是什么? 定义:state(vuex) ≍ data (vue) vuex的state和vue的data有很多相似之处,都是用于存储一些数据,或者说状态值.这些值都将被挂载 ...
- No identifier specified for entity: XXXX 错误
在运行项目的时候报了下面的错误: by: org.hibernate.AnnotationException: No identifier specified for entity: com.exam ...
- MFC自绘按钮的实现,按钮动态效果
最近项目需要实现按钮的动态效果,多方学习,现在终于能实现一些功能了. 过程如下: 第一,新建一MFC对话框应用程序. 第二,删除自带按钮,并添加两个按钮,button1,button2,ID为IDB_ ...
- python - work - 2
#-*- coding:utf-8 -*-# author:jiaxy# datetime:2018/11/3 11:48# software: PyCharm Community Edition d ...