- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Asp.Net Core利用xUnit进行主机级别的网络集成测试详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
前言 。
在开发 Asp.Net Core 应用程序的过程中,我们常常需要对业务代码编写单元测试,这种方法既快速又有效,利用单元测试做代码覆盖测试,也是非常必要的事情;但是,但我们需要对系统进行集成测试的时候,需要启动服务主机,利用浏览器或者Postman 等网络工具对接口进行集成测试,这就非常的不方便,同时浪费了大量的时间在重复启动应用程序上;今天要介绍就是如何在不启动应用程序的情况下,对 Asp.Net Core WebApi 项目进行网络集成测试.
1、建立项目 。
1.1 首先我们建立两个项目,Asp.Net Core WebApi 和 xUnit 单元测试项目,如下 。
1.2 上图的单元测试项目 Ron.XUnitTest 必须应用待测试的 WebApi 项目 Ron.TestDemo 。
1.3 接下来打开 Ron.XUnitTest 项目文件 .csproj,添加包引用 。
1
2
|
Microsoft.AspNetCore.App
Microsoft.AspNetCore.TestHost
|
1.4 为什么要引用这两个包呢,因为我刚才创建的 WebApi 项目是引用 Microsoft.AspNetCore.App 的,至于 Microsoft.AspNetCore.TestHost,它是今天的主角,为了使用测试主机,必须对其进行引用,下面会详细说明 。
2、编写业务 。
2.1 创建一个接口,代码如下 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
[Route(
"api/[controller]"
)]
[ApiController]
public
class
ValuesController : ControllerBase
{
private
IConfiguration configuration;
public
ValuesController(IConfiguration configuration)
{
this
.configuration = configuration;
}
[HttpGet(
"{id}"
)]
public
ActionResult<
int
> Get(
int
id)
{
var result= id +
this
.configuration.GetValue<
int
>(
"max"
);
return
result;
}
}
|
2.1 接口代码非常简单,接受一个参数 id,然后和配置文件中获取的值 max 相加,然后输出结果给客户端 。
3、编写测试用例 。
3.1 为了能够使用主机集成测试,我们需要使用类 。
1
|
Microsoft.AspNetCore.TestHost.TestServer
|
3.2 我们来看一下 TestServer 的源码,代码较长,你可以直接跳过此段,进入下一节 3.3 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
public
class
TestServer : IServer
{
private
IWebHost _hostInstance;
private
bool
_disposed =
false
;
private
IHttpApplication<Context> _application;
public
TestServer():
this
(
new
FeatureCollection())
{
}
public
TestServer(IFeatureCollection featureCollection)
{
Features = featureCollection ??
throw
new
ArgumentNullException(nameof(featureCollection));
}
public
TestServer(IWebHostBuilder builder):
this
(builder,
new
FeatureCollection())
{
}
public
TestServer(IWebHostBuilder builder, IFeatureCollection featureCollection):
this
(featureCollection)
{
if
(builder ==
null
)
{
throw
new
ArgumentNullException(nameof(builder));
}
var host = builder.UseServer(
this
).Build();
host.StartAsync().GetAwaiter().GetResult();
_hostInstance = host;
}
public
Uri BaseAddress {
get
;
set
; } =
new
Uri(
"http://localhost/"
);
public
IWebHost Host
{
get
{
return
_hostInstance
??
throw
new
InvalidOperationException(
"The TestServer constructor was not called with a IWebHostBuilder so IWebHost is not available."
);
}
}
public
IFeatureCollection Features {
get
; }
private
IHttpApplication<Context> Application
{
get
=> _application ??
throw
new
InvalidOperationException(
"The server has not been started or no web application was configured."
);
}
public
HttpMessageHandler CreateHandler()
{
var pathBase = BaseAddress ==
null
? PathString.Empty : PathString.FromUriComponent(BaseAddress);
return
new
ClientHandler(pathBase, Application);
}
public
HttpClient CreateClient()
{
return
new
HttpClient(CreateHandler()) { BaseAddress = BaseAddress };
}
public
WebSocketClient CreateWebSocketClient()
{
var pathBase = BaseAddress ==
null
? PathString.Empty : PathString.FromUriComponent(BaseAddress);
return
new
WebSocketClient(pathBase, Application);
}
public
RequestBuilder CreateRequest(
string
path)
{
return
new
RequestBuilder(
this
, path);
}
public
async Task<HttpContext> SendAsync(Action<HttpContext> configureContext, CancellationToken cancellationToken =
default
)
{
if
(configureContext ==
null
)
{
throw
new
ArgumentNullException(nameof(configureContext));
}
var builder =
new
HttpContextBuilder(Application);
builder.Configure(context =>
{
var request = context.Request;
request.Scheme = BaseAddress.Scheme;
request.Host = HostString.FromUriComponent(BaseAddress);
if
(BaseAddress.IsDefaultPort)
{
request.Host =
new
HostString(request.Host.Host);
}
var pathBase = PathString.FromUriComponent(BaseAddress);
if
(pathBase.HasValue && pathBase.Value.EndsWith(
"/"
))
{
pathBase =
new
PathString(pathBase.Value.Substring(0, pathBase.Value.Length - 1));
}
request.PathBase = pathBase;
});
builder.Configure(configureContext);
return
await builder.SendAsync(cancellationToken).ConfigureAwait(
false
);
}
public
void
Dispose()
{
if
(!_disposed)
{
_disposed =
true
;
_hostInstance.Dispose();
}
}
Task IServer.StartAsync<TContext>(IHttpApplication<TContext> application, CancellationToken cancellationToken)
{
_application =
new
ApplicationWrapper<Context>((IHttpApplication<Context>)application, () =>
{
if
(_disposed)
{
throw
new
ObjectDisposedException(GetType().FullName);
}
});
return
Task.CompletedTask;
}
Task IServer.StopAsync(CancellationToken cancellationToken)
{
return
Task.CompletedTask;
}
private
class
ApplicationWrapper<TContext> : IHttpApplication<TContext>
{
private
readonly
IHttpApplication<TContext> _application;
private
readonly
Action _preProcessRequestAsync;
public
ApplicationWrapper(IHttpApplication<TContext> application, Action preProcessRequestAsync)
{
_application = application;
_preProcessRequestAsync = preProcessRequestAsync;
}
public
TContext CreateContext(IFeatureCollection contextFeatures)
{
return
_application.CreateContext(contextFeatures);
}
public
void
DisposeContext(TContext context, Exception exception)
{
_application.DisposeContext(context, exception);
}
public
Task ProcessRequestAsync(TContext context)
{
_preProcessRequestAsync();
return
_application.ProcessRequestAsync(context);
}
}
}
|
3.3 TestServer 类代码量比较大,不过不要紧,我们只需要关注它的构造方法就可以了 。
1
2
3
4
|
public
TestServer(IWebHostBuilder builder)
:
this
(builder,
new
FeatureCollection())
{
}
|
3.4 其构造方法接受一个 IWebHostBuilder 对象,只要我们传入一个 WebHostBuilder 就可以创建一个测试主机了 。
3.5 创建测试主机和 HttpClient 客户端,我们在测试类 ValuesUnitTest 编写如下代码 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
class
ValuesUnitTest
{
private
TestServer testServer;
private
HttpClient httpCLient;
public
ValuesUnitTest()
{
testServer =
new
TestServer(
new
WebHostBuilder().UseStartup<Ron.TestDemo.Startup>());
httpCLient = testServer.CreateClient();
}
[Fact]
public
async
void
GetTest()
{
var data = await httpCLient.GetAsync(
"/api/values/100"
);
var result = await data.Content.ReadAsStringAsync();
Assert.Equal(
"300"
, result);
}
}
|
代码解释 。
这段代码非常简单,首先,我们声明了一个 TestServer 和 HttpClient 对象,并在构造方法中初始化他们; TestServer 的初始化是由我们 new 了一个 Builder 对象,并指定其使用待测试项目 Ron.TestDemo 中的 Startup 类来启动,这样我们能可以直接使用待测试项目的路由和管道了,甚至我们无需指定测试站点,因为这些都会在 TestServer 自动配置一个 localhost 的主机地址 。
3.7 接下来就是创建了一个单元测试的方法,直接使用刚才初始化的 HttpClient 对象进行网络请求,这个时候,我们只需要知道 Action 即可,同时传递参数 100,最后断言服务器输出值为:"300",回顾一下我们创建的待测试方法,其业务正是将客户端传入的 id 值和配置文件 max 值相加后输出,而 max 值在这里被配置为 200 。
3.8 运行单元测试 。
3.9 测试通过,可以看到,测试达到了预期的结果,服务器正确返回了计算后的值 。
4、配置文件注意事项 。
4.1 在待测试项目中的配置文件 appsettings.json 并不会被测试主机所读取,因为我们在上面创建测试主机的时候没有调用方法 。
1
|
WebHost.CreateDefaultBuilder
|
4.2 我们只是创建了一个 WebHostBuilder 对象,非常轻量的主机配置,简单来说就是无配置,如果对于 WebHost.CreateDefaultBuilder 不理解的同学,建议阅读我的文章 asp.netcore 深入了解配置文件加载过程. 。
4.3 所以,为了能够在单元测试中使用项目配置文件,我在 Ron.TestDemo 项目中的 Startup 类加入了下面的代码 。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
public
class
Startup
{
public
Startup(IConfiguration configuration, IHostingEnvironment env)
{
this
.Configuration =
new
ConfigurationBuilder()
.AddJsonFile(
"appsettings.json"
)
.AddEnvironmentVariables()
.SetBasePath(env.ContentRootPath)
.Build();
}
public
IConfiguration Configuration {
get
; }
// This method gets called by the runtime. Use this method to add services to the container.
public
void
ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IConfiguration>(
this
.Configuration);
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
}
|
4.4 其目的就是手动读取配置文件,重新初始化 IConfiguration 对象,并将 this.Configuration 对象加入依赖注入容器中 。
结语 。
示例代码下载 。
Ron.TestDemo.rar 。
总结 。
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我的支持.
原文链接:https://www.cnblogs.com/viter/p/10091816.html 。
最后此篇关于Asp.Net Core利用xUnit进行主机级别的网络集成测试详解的文章就讲到这里了,如果你想了解更多关于Asp.Net Core利用xUnit进行主机级别的网络集成测试详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我找不到任何关于我认为是一个相当明显的问题的信息。xunit.console.clr4 命令行 arg 帮助状态: usage: xunit.console.clr4 [options] usage
当我们使用 MSTest 时,我们有几个环境有自己的运行设置。由于 Microsoft 正在放弃 MSTest,我们将转向 xUnit。无论是通过运行设置还是命令行属性,我都需要一种在 xUnit 测
我试图使用 OpenCover(今天下载)来覆盖我的测试。这是我使用的命令行: OpenCover.Console.exe -target:"c:\Programmes2\xunit\xunit.co
我正在学习使用单元测试,我创建了一个项目,添加了 xunit 引用。 以及以下代码: namespace UnitTestProject { public partial class Form
Closed. This question does not meet Stack Overflow guidelines。它当前不接受答案。 想要改善这个问题吗?更新问题,以便将其作为on-topi
我有一个在 Windows 上构建的程序集 我想在 Linux 中的单声道上运行 xUnit 测试。 但是,我发现虽然其中 400 个测试可以(按顺序)运行,但某些测试要么挂起 xUnit 运行程序,
我有一个 F# 类库,其中包含使用 NuGet 安装的“xUnit.net”和“xUnit.net Runners”包。我有以下代码: module XUnitTest open Xunit [] l
I know that this question has been asked multiple times before I raise it again, However I still cou
我们使用的是 dotCover 2.7 和 xUnit.net 1.9.2。 在我的机器 (Windows 7) 和同事的机器 (Windows 8) 上,我们可以从命令行针对我们使用 xUnit.n
是否可以将 xUnit 与 LINQPad 一起使用? 能够首先为在 LINQPad 中设计的概念编写一些测试会很棒。这比添加另一个 ConsoleApp23423894238 更容易,只是为了能够快
我有一组需要共享状态的 xunit.net 测试。希望我希望这些测试能够并行运行。所以我希望运行者这样做: 创建共享夹具 运行并行所有使用该夹具的测试 在阅读 xunit 文档时,它说要在测试类之间共
我需要跳过基于类中某些 bool 条件的测试方法。是否可以?如何实现?我试过扩展 FactAttribute 但我无法获得 Test 类的实例。 我的代码如下: using System; using
我有兴趣在多个类上重用测试理论,特别是一些需要相同测试的构造函数。我最初有使用委托(delegate)来执行此功能的想法。 但是,我认为我可能正在尝试重新发明轮子,尽管 C# 具有一些功能,但我认为我
我正在为我的 DataAccessRepository(使用 Entity Framework )类编写测试用例。此类在构造函数中采用两个参数。 1)连接对象 2) Automapper 对象 现在,
xUnit 相当于 NUnit 的 [TestFixtureSetUp] ? 我们探索发现IUseFixture相当于[TestFixtureSetUp] ,但它没有按预期工作。 正如我们所探索的(在
Xunit 1.9.x 为用户提供了 DynamicSkipExample.cs 帮助他设置动态跳过 [Fact] 的示例。 事实证明,这在执行一些跨平台开发时非常有用。当由于底层上下文(操作系统、文
我正在尝试使用 xUnit.net 作为 SpecFlow 的测试运行器。来自官方下载区的 SpecFlow 1.2 二进制文件不包含 xUnit.net 提供程序,但 GitHub 上的主分支有一个
当我尝试运行我的 xUnit.net 测试时,出现此错误: [xUnit.net 00:00:00.63] xunit.UnitTest1.TestTheAnswer [FAIL] Fai
我已经转移到 xunit.net 2.0 测试版,但需要 AutoFixture 的一些功能,这仍然取决于当前的 1.9.2 稳定版本 (CompositeDataAttribute)。据我所知 Au
我已经使用 NUnit 多年了,我想尝试 XUnit。所以我安装了 XUnit 并运行了允许您通过 TD.net 运行 XUnit 的可执行文件。 我似乎不能一次运行多个测试。使用 NUnit + T
我是一名优秀的程序员,十分优秀!