译者: Akini Xu
原文: Converting integration tests to .NET Core 3.0
作者: Andrew Lock
此文是 升级至 ASP.NET Core 3.0 第5篇:
- 转换.NET Standard 2.0类库到.NET Core 3.0
- 对比IHostingEnvironment与IHostEnvironment .NET及Core 3.0中的过时类型
- 不要在Startup类的构造函数中使用依赖注入
- 将末端中间件转换为端点路由
- 将集成测试升级至.NET Core 3.0
在本文中,我们来讨论一下,当升级到ASP.NET Core 3.0后,集成测试代码中WebApplicationFactory<>
或TestServer
的变化。
ASP.NET Core 3.0的最大变化之一,是在通用主机架构上运行,而不是在WebHost上。 在本系列的前几篇文章及探索ASP.NET Core 3.0的系列中,我们已经解决了一部分升级后带来的问题,我们来看看对周边的基础设施有哪些影响,例如用于集成测试的TestServer
。
使用Test Host和TestServer来做集成测试
ASP.NET Core包含Microsoft.AspNetCore.TestHost库,它是一个可以在内存中运行的Web主机。
这里面用了容易让人混淆的术语。内存中的主机和NuGet包通常被称为“ TestHost”,而在代码中使用的实际类是TestServer。 两者经常互换使用。
在ASP.NET Core 2.x中,可以将IWebHostBuilder
实例作为参数,传递给TestServer
构造函数来创建测试服务器:
public class TestHost2ExampleTests |
在上面的示例中,我们创建了一个简单的WebHostBuilder
,针对所有的Url请求,都返回“ Hello World!”。 然后,使用TestServer
创建一个内存服务器:
var server = new TestServer(webHostBuilder); |
最后,我们创建一个HttpClient
对象,发送HTTP请求发送到内存服务器。
var client = server.CreateClient(); |
在.NET core 3.0中,写法还是类似的,但是由于迁移到通用主机,使用方法稍微复杂一些。
.NET core 3.0中的TestServer
如果要升级.NET Core 2.x测试项目到.NET Core 3.0,编辑项目的*.csproj*,然后将<TargetFramework>
元素更改为netcoreapp3.0
。 接下来,将Microsoft.AspNetCore.App的<PackageReference>
替换为<FrameworkReference>
,并将其他软件包版本更新为3.0.0
。
升级至.NET Core 3.0项目后,项目运行没有任何错误,并且可以通过测试。 但是,该代码中使用的是WebHost
,而不是通用主机。 下面改为使用通用主机。
首先,使用HostBuilder替代WebHostBuilder:
var hostBuilder = new HostBuilder(); |
HostBuilder
中没有Configure()
方法。改成先调用ConfigureWebHost()
,然后在内部IWebHostBuilder
对象上调用Configure()
。
var hostBuilder = new HostBuilder() |
改完后,TestServer
构造函数这里编译报错。
TestServer
构造函数参数类型是IWebHostBuilder
,在通用主机中只有IHostBuilder
。 我花了一些时间才发现根本不用手动创建TestServer
:
- 在
ConfigureWebHost
中调用UseTestServer()
以添加TestServer
实现。 - 在
IHostBuilder
上调用StartAsync()
方法来构建和启动一个IHost
实例。 - 在已启动的
IHost
实例上调用GetTestClient()
获取HttpClient
实例。
最终的代码如下:
public class TestHost3ExampleTests |
如果没有执行UseTestServer()
,运行时会看到下面错误:
System.InvalidOperationException : Unable to resolve service for type 'Microsoft.AspNetCore.Hosting.Server.IServer' while attempting to activate 'Microsoft.AspNetCore.Hosting.GenericWebHostService' |
使用WebApplicationFactory集成测试
像上面那样直接使用TestServer
非常方便,但是对于实际项目的集成测试则不太方便。 Microsoft.AspNetCore.Mvc.Testing需要处理一些比较棘手问题,例如设置ContentRoot路径,将*.deps文件复制到测试项目的bin*文件夹等。使用WebApplicationFactory<>
可以简化TestServer
的创建过程。
如何使用WebApplicationFactory<>的文档,在.NET Core 3.0任然适用。 但是,ASP.NET Core 2.x升级到3.0时,还是需要一些调整。
在ASP.NET Core 2.x中使用WebApplicationFactory添加XUnit日志
假设你的程序是按下面步骤设置的:
- 通过
dotnet new webapp
创建的一个.NET Core Razor应用程序。 - 针对Razor应用程序项目创建了一个集成测试项目。
这个演示项目的源码在Github中。
您可以按照文档的说明直接在测试中使用WebApplicationFactory<>
类。 但有时候我们想自定义使用自定义的WebApplicationFactory<>
,比如,替换一些Mock服务、自动运行数据库迁移、自定义IHostBuilder
。
使用xUnit ITestOutputHelper,将日志输出至ILogger
中,方便查看TestServer
中的错误信息。 Martin Costello提供的Nuget包MartinCostello.Logging.XUnit比较方便做集成。代码如下:
public class ExampleAppTestFixture : WebApplicationFactory<Program> |
ExampleAppTestFixture
做了2件事:
- 从容器中删除所有的
IHostedServices
(后台运行的服务),以便它们在集成测试期间不会运行。通常我们不希望,在测试时后台服务自动订阅或消费RabbitMQ/KafKa的消息。 - 使用
ITestOutputHelper
xUnit的日志与ASP.NET Core基础设施的ILogger
挂接。
要在测试中使用ExampleAppTestFixture
,必须在测试类上实现IClassFixture<T>
接口,将ExampleAppTestFixture
通过构造函数注入,并设置Output属性。
public class HttpTests: IClassFixture<ExampleAppTestFixture>, IDisposable |
示例中请求RazorPages应用程序的主页,并在body中查找字符串“ Welcome”(位于
标记中)。 应用程序生成的日志都通过管道传递到xUnit的输出,这使得易于理解集成测试失败时发生的原因:
[2019-10-29 18:33:23Z] info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
[2019-10-29 18:33:23Z] info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
...
[2019-10-29 18:33:23Z] info: Microsoft.AspNetCore.Routing.EndpointMiddleware[1]
Executed endpoint '/Index'
[2019-10-29 18:33:23Z] info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
Request finished in 182.4109ms 200 text/html; charset=utf-8
在ASP.NET Core 3.0中使用WebApplicationFactory
[2019-10-29 18:33:23Z] info: Microsoft.Hosting.Lifetime[0] |
将集成测试项目转换为目标.NET Core 3.0之后,似乎不需要进行任何更改。 但是要注意,ExampleAppTestFixture
的CreateWebHostBuilder()
方法永远不会被调用。
原因是WebApplicationFactory
需要同时支持Web主机和通用主机。 如果在Program.cs中使用了WebHostBuilder
,则工厂将调用CreateWebHostBuilder()
并运行重写的方法。 但是,如果使用的是通用HostBuilder
,则工厂将调用另一种方法CreateHostBuilder()
。
修改Factory的代码,将CreateWebHostBuilder
重命名为CreateHostBuilder
,将返回类型从IWebHostBuilder
更改为IHostBuilder
,然后将调用更改为使用通用主机。 其他所有内容保持不变:
public class ExampleAppTestFixture : WebApplicationFactory<Program> |
请注意,
ConfigureWebHost
方法不会更改-在两种情况下都会被调用,参数任然是IWebHostBuilder
。
全部修改完成后,日志可以正常输出了。
总结
在这篇文章中,我描述了将应用程序从ASP.NET Core 2.1迁移到ASP.NET Core 3.0之后,集成测试中所需的一些更改。 仅且使用通用主机时,才需要进行这些更改。 如果要迁移到使用通用主机,则需要修改TestServer
或WebApplicationFactory
的代码。
修改TestServer
代码,在HostBuilder.ConfigureWebHost()
方法内调用UseTestServer()
。 然后构建主机,并调用StartAsync()
启动主机。 最后,调用IHost.GetTestClient()
创建一个HttpClient
。
修改自定义的WebApplicationFactory
代码,如果使用WebHost
,需要重写CreateWebHostBuilder
方法。使用通用Host
,需要重写CreateWebHostBuilder方法。