ASP.NET Core Web API 集成测试
本文需要您了解ASP.NET Core Web API 和 xUnit的相关知识.
这里有xUnit的介绍: https://www.cnblogs.com/cgzl/p/9178672.html#test
ASP.NET Core集成测试官方文档: https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.1
集成测试 vs 单元测试

测试金字塔, 但它只是一个指导性的概念.
如果所单元测试是对一个组件进行隔离测试的话, 那么集成测试则是测试多个组件共同协作产生出期待的结果.
单元测试通常很快. 而集成测试则慢的多, 因为它需要很多配置, 并且可能依赖于外部的组件, 例如数据库, 网络, 文件等.
通常在一个项目里单元测试要比集成测试多很多.
单元测试通常依赖于mock的组件, 而集成测试则使用可运行的组件.
注意: 如果一个行为可以通过单元测试或集成测试来测试的话, 那么应该使用单元测试.
如何进行集成测试
如果我想测试一个API Controller的Action, 我可能需要把这个项目运行起来, 等它跑起来, 发送请求并检验结果. 但这样做的话需要很多的配置工作, 并且很麻烦.
幸好ASP.NET Core 提供了一个Microsoft.AspNetCore.TestHost 库, 使用它就无需单独去运行被测试系统了.
ASP.NET Core应用里, 我们在Program.cs里创建WebHostBuilder, 并配置Kestrel Web服务器, 使用Startup类进行应用配置, 注册服务和中间件等. 最终在WebHostBuilder上使用Build()来创建WebHost的实例, 它可以用来在特定的URL和端口上运行并监听请求.
而这个TestHost库也使用了WebHostBuilder, 但它会自己把构建和运行web宿主的工作处理好, 也就是创建出了一个TestServer. TestServer不会在网络上进行监听, TestServer创建了一个名为Host的属性, 它的类型是IWebHost, 它可以用来处理内存里的请求对象. TestServer还会暴露一个HttpClient, 你可以用它来发送请求到被测试系统. 整个交互的过程都是在内存里完成的.
下图是被测试系统在生产环境和集成测试使用TestServer情形下的对比图:

图中:
当应用/被测试系统在生产环境运行的时候, 它使用Kestrel服务器, 监听HTTP请求, 并把它转化为HttpContext, 然后再传进ASP.NET Core的管道里.
TestServer不监听网络请求, 它使用HttpClient在内存里发送请求.
仔细看一下集成测试时使用TestServer的流图:

图中可以看到: 测试代码创建TestServer, TestServer创建HttpClient. 测试代码使用HttpClient发送请求接收响应. TestServer会转化请求并交给ASP.NET Core MVC/API 应用来处理.
一个例子
首先需要为你的应用建立集成测试项目:

然后需要为项目添加Microsoft.AspNetCore.TestHost 这个库:

被测试的是这个Controller的GetRoot()所对应的行为, 而不只是这个方法:

测试返回NoContent:

这里面按照之前讲的顺序, 创建IWebHostBuilder, 并用它创建TestServer, 然后TestServer创建HttpClient. 随后就使用httpClient发送请求, 返回结果, Assert即可.
需要注意的是, 在创建IWebHostBuilder的时候, 我使用了被测试系统的Startup类来进行配置, 并设定的环境是Development.
由于我这个项目可以看作是真实项目, 所以第一次运行该测试的时候, 测试是Fail的. 因为Startup里面有很多配置并不满足测试要求.
在我把IpRateLimiting, HttpsRedirection, Authentication, AuthorizeFilter等中间件/组件去掉之后, 测试才通过:

所以这就引出了一个问题, Startup里面的配置在开发时 和 测试时 以及 生产运行时 可能是不太一样的.
我的Startup里面已经有很多代码了, 如果再进行环境判断, 那就会更乱了.
所以我决定为集成测试新建立一个Startup配置类:

ASP.NET Core项目也支持多环境的多个Startup配置类, 这部分内容请参考官方文档: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/environments?view=aspnetcore-2.1#environment-based-startup-class-and-methods.
然后修改代码, 使用这个测试专用的Startup即可:

测试会通过.
被测试系统有依赖项
下面继续测试GetRoot方法的另一个路径, 这个路径会用到RootController的依赖项IUrlHelper.
在集成测试里, 通常情况下是不使用Mocking技术的. 所以在这里我也不会mock IUrlHelper:

这里没有mock任何东西. 此外这个被测试的行为需要设置AcceptHeader.
测试会Pass的, TestServer帮我搞定了一切:

优化测试配置
写了两个测试方法, 又引出了一个新的问题: 这两个方法有一些共同的设置代码, 这些设置可能会比较耗资源. 我们可以把这些设置放在构造函数里面, 但是如果使用Theory并含有多个InlineData的话, 就会多次运行构造函数里的设置代码, 可能会非常好资源(时间).
所以我们应该考虑使用test fixture 这里有介绍: http://www.cnblogs.com/cgzl/p/8438019.html#share
而且我们可以使用WebApplicationFactory来构建TestServer, 使用WebApplicationFactory的好处是可以灵活的进行自定义配置.
要使用WebApplicationFactory, 需要添加库: Microsoft.AspNetCore.Mvc.Testing

使用该库之后, 代码应该如下:

但是却有一个问题, 这里我选择的时StartupIntegrationTest. 而电脑环境变量设置的是Development, 而调试测试之后发现走的是StartupDevelopment.
也许这是个Bug? 或者就是这样的意图. 那我暂时还是使用原始的方法创建TestServer吧, 下面是我使用的代码:
建立一个TestServerFixture, 需要使用IDisposable来做清理工作:

而测试类注入该Fixture即可:

然后重跑测试, 会pass的:

一个复杂点的例子


我要测试这个Controller下CreateProduct方法对应的行为. 该Controller需要很多依赖项, 其中两个还需要使用数据库.
通常情况下集成测试里使用的数据库和生产环境中使用的数据库不同, 在测试环境我更倾向于使用内存类数据库.
EF Core里面至少有两个内存类的数据库提供商:
- Microsoft.EntityFrameworkCore.InMemory, 这个都应该知道.
- Microsoft.EntityFrameworkCore.Sqlite. 虽然说Sqlite通常是把数据保存到文件, 但是提供商为它提供了一个内存模式, 把数据库保存到了内存里.
在StartupIntegrationTest里, 我就使用InMemory吧;

下面是测试方法的代码:

这代码其实很简单, 就是对应着被测试的Controller方法做一些需要的设定即可, 例如Headers, Content-Type等等.
需要注意的是Content-Type是在Content的Header里设置, 而不是Request的Headers里设置, 否则会报乱用Header的错.
该测试会pass:

最后针对该行为再做一个Model验证失败的测试:

没什么不同, 就是model的Name属性超长了.
这个测试同样会通过:

集成测试就简单介绍这些.......
ASP.NET Core Web API 集成测试的更多相关文章
- ASP.NET Core Web API 集成测试中使用 Bearer Token
在 ASP.NET Core Web API 集成测试一文中, 我介绍了ASP.NET Core Web API的集成测试. 在那里我使用了测试专用的Startup类, 里面的配置和开发时有一些区别, ...
- ASP.NET Core Web API 索引 (更新Identity Server 4 视频教程)
GraphQL 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(上) 使用ASP.NET Core开发GraphQL服务器 -- 预备知识(下) [视频] 使用ASP.NET C ...
- 使用 Swagger 自动生成 ASP.NET Core Web API 的文档、在线帮助测试文档(ASP.NET Core Web API 自动生成文档)
对于开发人员来说,构建一个消费应用程序时去了解各种各样的 API 是一个巨大的挑战.在你的 Web API 项目中使用 Swagger 的 .NET Core 封装 Swashbuckle 可以帮助你 ...
- 在ASP.NET Core Web API上使用Swagger提供API文档
我在开发自己的博客系统(http://daxnet.me)时,给自己的RESTful服务增加了基于Swagger的API文档功能.当设置IISExpress的默认启动路由到Swagger的API文档页 ...
- Docker容器环境下ASP.NET Core Web API应用程序的调试
本文主要介绍通过Visual Studio 2015 Tools for Docker – Preview插件,在Docker容器环境下,对ASP.NET Core Web API应用程序进行调试.在 ...
- 在docker中运行ASP.NET Core Web API应用程序
本文是一篇指导快速演练的文章,将介绍在docker中运行一个ASP.NET Core Web API应用程序的基本步骤,在介绍的过程中,也会对docker的使用进行一些简单的描述.对于.NET Cor ...
- ASP.NET Core Web API Cassandra CRUD 操作
在本文中,我们将创建一个简单的 Web API 来实现对一个 “todo” 列表的 CRUD 操作,使用 Apache Cassandra 来存储数据,在这里不会创建 UI ,Web API 的测试将 ...
- 在Mac下创建ASP.NET Core Web API
在Mac下创建ASP.NET Core Web API 这系列文章是参考了.NET Core文档和源码,可能有人要问,直接看官方的英文文档不就可以了吗,为什么还要写这些文章呢? 原因如下: 官方文档涉 ...
- ASP.NET Core Web API 开发-RESTful API实现
ASP.NET Core Web API 开发-RESTful API实现 REST 介绍: 符合REST设计风格的Web API称为RESTful API. 具象状态传输(英文:Representa ...
随机推荐
- python爬虫的重定向问题
重定向问题 在使用python爬虫的过程中难免会遇到很多301,302的问题.他们出现时,很大程度的影响到我们的爬虫速度和信息的准确性.下面针对不同的模块给出不同的解决方案. 使用requests模块 ...
- 【爆料】-《悉尼大学毕业证书》USYD一模一样原件
☞悉尼大学毕业证书[微/Q:2544033233◆WeChat:CC6669834]UC毕业证书/联系人Alice[查看点击百度快照查看][留信网学历认证&博士&硕士&海归&a ...
- 深入理解Java虚拟机 第三章 垃圾收集器 笔记
1.1 垃圾收集器 垃圾收集器是内存回收的具体实现.以下讨论的收集器是基于JDK1.7Update14之后的HotSpot虚拟机.这个虚拟机包含的所有收集器有: 上图展示了7种作用于不同分代的收集 ...
- js面试题1
1.介绍js的基本数据类型 Undefined.Null.Boolean.Number.String 2.js有哪些内置对象? 数据封装类对象:Object.Array.Boolean.Number ...
- 浅谈URL跳转与Webview安全
学习信息安全技术的过程中,用开阔的眼光看待安全问题会得到不同的结论. 在一次测试中我用Burpsuite搜索了关键词url找到了某处url,测试一下发现waf拦截了指向外域的请求,于是开始尝试绕过.第 ...
- 树莓派3b+_32位linux系统arm架构安装JDK
如图我的Raspbian系统如下图版本信息: 可以看到是armv7l,我查了一下是32位的arm架构,即下载第一个就好了 然后用SSH Secure Shell远程上去把压缩包或者解压后的文件传过去 ...
- Python档案袋(生成器、迭代器、队列 )
生成器: 简单的生成器实现: #生成器,将for循环的变量传递到前面的式子进行处理 #生成的并不是一个列表,而是一个存在算数规则的对象 #不能通过下标直接取值,必须一个一个从头到尾取 va=(i*2 ...
- 使用github pages搭建个人博客
一.环境准备 使用Github Pages搭建个人博客,一劳永逸,可以让我们更加专注于博客的撰写.博客的更新是通过将新建或改动的博客放在指定文件夹并推送到远程Github仓库来完成的,所以我们本地需要 ...
- 前端笔记之NodeJS(三)Express&ejs模板引擎&请求识别
一.Express框架 1.1基本使用 创建http服务器特别麻烦,express框架解决了这个的问题. Express在node界的地位,就相当于jQuery在DOM界的地位.jQuery的核心就是 ...
- video 属性和事件用法大全
1.video 属性 <!-- video 不支持 IE8及以下版本浏览器,支持三种视频格式:MP4,WebM 和 Ogg --> <video src="test.mp4 ...