WebApi的一种集成测试写法(in-memory)
WebApi的一种集成测试写法(in-memory)
大家是如何对webApi写测试的呢?
1.利用Fiddler直接做请求,观察response的内容。
2.利用Httpclient做请求,断言response的内容。
3.直接调用webApi的action,这种方式的测试跟真实的调用还是有一定差距,不够完美。
接下来我介绍一种webApi的in-memory调用方法,也能够达到对webApi的测试,并且由于是in-memory调用,效率也比较高,非常适写单元测试。本文参考了In memory client, host and integration testing of your Web API service。
一、首先写一个OrderController用来做测试用
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
|
public class OrderController : ApiController { // GET api/order public Order Get() { return new Order(){Id = 1,Descriptions = "descriptions" ,Name = "name" }; } // GET api/order/5 public string Get( int id) { return "value" ; } // POST api/order public Order Post(Order order) { return order; } // DELETE api/order/5 public void Delete( int id) { } } |
二、WebApi的请求过程
webApi的核心是对消息的管道处理,整个核心是有一系列消息处理器(HttpMessageHandler)首尾连接的双向管道,管道头为HttpServer,管道尾为HttpControllerDispatcher,HttpControllerDispatcher负责对controller的激活和action的执行,然后相应的消息逆向流出管道。
所以我们可以利用HttpMessageInvoker将一个请求消息HttpRequestMessage发送到管道中,最后收到的消息HttpResponseMessage就代表一个真实的请求响应。
三、Get请求的测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
[Test] public void GetTest() { HttpConfiguration config = new HttpConfiguration(); WebApiConfig.Register(config); config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; HttpServer server = new HttpServer(config); HttpMessageInvoker messageInvoker = new HttpMessageInvoker(server); CancellationTokenSource cts = new CancellationTokenSource(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, baseAddress + "api/order" ); using (HttpResponseMessage response = messageInvoker.SendAsync(request, cts.Token).Result) { var content = response.Content.ReadAsStringAsync().Result; var result = JsonConvert.DeserializeObject<Order>(content); result.Name.Should().Be( "name" ); } } |
四、Post请求的测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
[Test] public void PostTest() { HttpConfiguration config = new HttpConfiguration(); WebApiConfig.Register(config); config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always; HttpServer server = new HttpServer(config); HttpMessageInvoker messageInvoker = new HttpMessageInvoker(server); CancellationTokenSource cts = new CancellationTokenSource(); HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, baseAddress + "api/order" ); var order = new Order() { Id = 1, Name = "orderName" , Descriptions = "orderDescriptions" }; request.Content = new ObjectContent<Order>(order, new JsonMediaTypeFormatter()); using (HttpResponseMessage response = messageInvoker.SendAsync(request, cts.Token).Result) { var content = JsonConvert.SerializeObject(order, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() }); response.Content.ReadAsStringAsync().Result.Should().Be(content); } } |
四、重构
可以看到这两个测试大部分的代码是相同的,都是用来发送请求。因此我们提取一个webApiTestBase类,该基类可以提供InvokeGetRequest,InvokePostRequest,InvokePutRequest等方法
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
|
public abstract class ApiTestBase { public abstract string GetBaseAddress(); protected TResult InvokeGetRequest<TResult>( string api) { using ( var invoker = CreateMessageInvoker()) { using ( var cts = new CancellationTokenSource()) { var request = new HttpRequestMessage(HttpMethod.Get, GetBaseAddress() + api); using (HttpResponseMessage response = invoker.SendAsync(request, cts.Token).Result) { var result = response.Content.ReadAsStringAsync().Result; return JsonConvert.DeserializeObject<TResult>(result); } } } } protected TResult InvokePostRequest<TResult, TArguemnt>( string api, TArguemnt arg) { var invoker = CreateMessageInvoker(); using ( var cts = new CancellationTokenSource()) { var request = new HttpRequestMessage(HttpMethod.Post, GetBaseAddress() + api); request.Content = new ObjectContent<TArguemnt>(arg, new JsonMediaTypeFormatter()); using (HttpResponseMessage response = invoker.SendAsync(request, cts.Token).Result) { var result = response.Content.ReadAsStringAsync().Result; return JsonConvert.DeserializeObject<TResult>(result); } } } private HttpMessageInvoker CreateMessageInvoker() { var config = new HttpConfiguration(); WebApiConfig.Register(config); var server = new HttpServer(config); var messageInvoker = new HttpMessageInvoker(server); return messageInvoker; } } |
有了这个基类,我们写测试只需要重写方法GetBaseAddress(),然后直接调用基类方法并进行断言即可
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
|
[TestFixture] public class OrderApiTests:ApiTestBase { public override string GetBaseAddress() { } [Test] public void Should_get_order_successfully() { var result = InvokeGetRequest<Order>( "api/order" ); result.Name.Should().Be( "name" ); result.Descriptions.Should().Be( "descriptions" ); result.Id.Should().Be(1); } [Test] public void Should_post_order_successfully() { var newOrder= new Order(){Name = "newOrder" ,Id = 100,Descriptions = "new-order-description" }; var result = InvokePostRequest<Order,Order>( "api/order" , newOrder); result.Name.Should().Be( "newOrder" ); result.Id.Should().Be(100); result.Descriptions.Should().Be( "new-order-description" ); } } |
是不是干净多了。
这种in-memory的测试方案有什么优点和缺点呢?
优点:
1.模拟真实调用,需要传入api地址即可得到结果,由于整个调用是in-memory的,所有效率很高,很适合集成测试。
2.整个测试时可以调试的,可以直接从单元测试调试进去,如果你写一个httpClient的测试,需要把webApi启动起来,然后。。。麻烦
缺点:我觉得原文作者说的那些缺点都可以忽略不计^_^
WebApi的一种集成测试写法(in-memory)的更多相关文章
- SQL Server 存储过程中处理多个查询条件的几种常见写法分析,我们该用那种写法
本文出处: http://www.cnblogs.com/wy123/p/5958047.html 最近发现还有不少做开发的小伙伴,在写存储过程的时候,在参考已有的不同的写法时,往往很迷茫,不知道各种 ...
- 查询分页的几种Sql写法
查询分页的几种Sql写法 摘自:http://www.cnblogs.com/zcttxs/archive/2012/04/01/2429151.html 1.创建测试环境,(插入100万条数据大概耗 ...
- 转--Android按钮单击事件的四种常用写法总结
这篇文章主要介绍了Android按钮单击事件的四种常用写法总结,比较了常见的四种写法的优劣,有不错的参考借鉴价值,需要的朋友可以参考下 很多学习Android程序设计的人都会发现每个人对代码的 ...
- 代码片段--Makefile之大型工程项目子目录Makefile的一种通用写法
转载:http://blog.csdn.net/mo_hui123456/article/details/8929615 管理Linux环境下的C/C++大型项目,如果有一个智能的Build Syst ...
- Makefile之大型工程项目子目录Makefile的一种通用写法
管理Linux环境下的C/C++大型项目,如果有一个智能的Build System会起到事半功倍的效果,本文描述Linux环境下大型工程项目子目录Makefile的一种通用写法,使用该方法,当该子目录 ...
- Makefile之大型工程项目子目录Makefile的一种通用写法【转】
转自:http://www.cnblogs.com/skyofbitbit/p/3680753.html 管理Linux环境下的C/C++大型项目,如果有一个智能的Build System会起到事半功 ...
- Android按钮单击事件的四种常用写法
这篇文章主要介绍了Android按钮单击事件的四种常用写法总结,比较了常见的四种写法的优劣,有不错的参考借鉴价值,需要的朋友可以参考下 很多学习Android程序设计的人都会发现每个人对代码的写法都有 ...
- SQL Server 存储过程的几种常见写法分析,我们该用那种写法
本文出处: http://www.cnblogs.com/wy123/p/5958047.html 最近发现还有不少做开发的小伙伴,在写存储过程的时候,在参考已有的不同的写法时,往往很迷茫,不知道各种 ...
- js中的三种函数写法
js中的三种函数写法 <script type="text/javascript"> //普通的声明方式 function myFun(m,n){ alert(m+n) ...
随机推荐
- ajax j跨域请求sonp
需求 遇到的问题 解决方案 需求 如今,该项目需要获得数据访问外部链接.它是跨域.使用ajax 直提示: 遇到的问题 1. 怎样使用ajax 跨域请求数据 2. 能不能post请求 解决的方法 经过网 ...
- Gradle 1.12 翻译——第十六章. 使用文件
有关其它已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或訪问:http://gradledoc.qiniudn.com ...
- 《图书管理系统——java》
/* (程序头部凝视開始) * 程序的版权和版本号声明部分 * Copyright (c) 2011, 烟台大学计算机学院学生 * All rights reserved. * 文件名: < ...
- iOS 辛格尔顿
单例模式: 为什么使用单例,单例模式的用途是什么?以下我们举一个样例来诠释一下 举个大家都熟知的样例--Windows任务管理器,如图,我们能够做一个这种尝试,在Windows的"任务栏&q ...
- QUIC简要
QUIC.即Quick UDP Internet Connection,类似于SPDY,相同也是由Google公司在现有已存协议之上进行了扩展设计,而旨在降低网络延迟.之前我曾介绍过SPDY的相关信息 ...
- 探索C/C++大数快(自然数)模板
本文fcbruce个人原创整理.转载请注明出处http://blog.csdn.net/u012965890/article/details/40432511,谢谢. 我们知道在C/C++中int型可 ...
- struts2移除标签button的id传统的价值观念问题
<!--显示数据列表--> <tbody id="TableData" class="dataContainer" datakey=" ...
- SQL_substr功能测试
原创作品.从 "深蓝blog" 博客,欢迎转载,请务必注明转载的来源.权法律责任. 深蓝的blog:http://blog.csdn.net/huangyanlong/articl ...
- IndexReader已解决的问题
设计和实时搜索的发展,IndexReader饮酒数成为0当调用doClose,和SegmentReader再有一个addCoreClosedListener控制的方法SegmentCoreReader ...
- ios新开发语言swift 新手教程
http://gashero.iteye.com/blog/2075324 视频教程:http://edu.51cto.com/lesson/id-26464.html