WebAPI的一种单元测试方案
大家是如何对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用来做测试用
- 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请求的测试
- [Test]
- public void GetTest()
- {
- string baseAddress = "http://localhost:33203/";
- 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请求的测试
- [Test]
- public void PostTest()
- {
- string baseAddress = "http://localhost:33203/";
- 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等方法
- 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(),然后直接调用基类方法并进行断言即可
- [TestFixture]
- public class OrderApiTests:ApiTestBase
- {
- public override string GetBaseAddress()
- {
- return "http://localhost:33203/";
- }
- [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的一种单元测试方案的更多相关文章
- WebApi的一种集成测试写法(in-memory)
WebApi的一种集成测试写法(in-memory) 大家是如何对webApi写测试的呢? 1.利用Fiddler直接做请求,观察response的内容. 2.利用Httpclient做请求,断言 ...
- 总结:视频播放的四种实现方案(Native)
一.来自 AVFoundation的 AVPlayer对象 特点: 1. AVPlayer > 优点: 可以自定义UI, 进行控制 > 缺点: ...
- 转【实战体验几种MySQLCluster方案】
实战体验几种MySQLCluster方案 1.背景 MySQL的cluster方案有很多官方和第三方的选择,选择多就是一种烦恼,因此,我们考虑MySQL数据库满足下三点需求,考察市面上可行的解决方案: ...
- 真正意义上的spring环境中的单元测试方案spring-test与mokito完美结合
真正意义上的spring环境中的单元测试方案spring-test与mokito完美结合 博客分类: java 测试 单元测试SpringCC++C# 一.要解决的问题: spring环境中 ...
- 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案
一. 简介 揭秘: SimpleThreadPool是Quartz.Net中自带的线程池,默认个数为10个,代表一个Scheduler同一时刻并发的最多只能执行10个job,超过10个的job需要排队 ...
- [转CSDN多篇文章]WEB 3D SVG CAD 矢量 几种实现方案
WEB 3D SVG CAD 矢量 几种实现方案 原创 2014年10月24日 08:34:11 标签: WEB3D / CADSVG / 矢量 2665 一.全部自己开发,从底层开始 VML+SVG ...
- 分布式唯一ID的几种生成方案
前言 在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID.退款ID等.那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十 ...
- 第九节: 利用RemoteScheduler实现Sheduler的远程控制 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案 第六节: 六类Calander处理六种不同的时间场景 第五节: Quartz.Net五大构件之Trigger的四大触发类 第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联
第九节: 利用RemoteScheduler实现Sheduler的远程控制 一. RemoteScheduler远程控制 1. 背景: 在A服务器上部署了一个Scheduler,我们想在B服务器上 ...
- JavaScript常用八种继承方案
更新:在常用七种继承方案的基础之上增加了ES6的类继承,所以现在变成八种啦,欢迎加高级前端进阶群一起学习(文末). --- 2018.10.30 1.原型链继承 构造函数.原型和实例之间的关系:每个构 ...
随机推荐
- Codeforces 586D. Phillip and Trains 搜索
D. Phillip and Trains time limit per test: 1 second memory limit per test :256 megabytes input: stan ...
- 影响google PageRank的因素
1 与pr高的网站做链接: 2 内容质量高的网站链接 3 加入搜索引擎分类目录 4 加入免费开源目录 5 你的链接出现在流量大.知名度高.频繁更新的重要网站上 6 google对PDF格式的文件比较看 ...
- Error:SSL peer shut down incorrectly
从别的地方拷贝过来的项目有时会报这个错误,解决方法 File -> Project Structure -> project 对比本地项目和拷贝项目并修改至与本地项目一致
- 【转载】PHP 开发者该知道的 5 个 Composer 小技巧
Composer是新一代的PHP依赖管理工具.其介绍和基本用法可以看这篇<Composer PHP依赖管理的新时代>.本文介绍使用Composer的五个小技巧,希望能给你的PHP开发带来方 ...
- prism4 StockTrader RI 项目分析一些体会
StockTrader RI 代码分析也有一段时间了 刚从codeplex获取到源代码的时候,看得一头雾水,不知所云(因为之前没做过wpf的项目,看文档也静不下那心来看) 后来就想了个笨办法,模拟项目 ...
- php特性包括哪些?
PHP的特性包括: 1. PHP 独特的语法混合了 C.Java.Perl 以及 PHP 自创新的语法. 2. PHP可以比CGI或者Perl更快速的执行动态网页——动态页面方面,与其他的编程语言相比 ...
- sql server 2000数据库 最近经常出现某进程一直占用资源,阻塞?死锁?
OA的数据库最近多次出现某进程一直占用资源,导致其他进程无法执行.使用sp_who2 和 sql server profiler跟踪查询,发现有以下几个语句常常占用资源: 1.declare @P1 ...
- checkbox标签已有checked=checked属性但是不显示勾选
点击全选按钮,选中下面的列表,再次点击取消选择. 第一次的使用的方法是$("input[name=xxx]").attr('checked',true); 但是往往刷新页面第一次点 ...
- C# 将短时间格式变长正常时间格式
DateTime dateTimeStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); ...
- [转] 利用SET STATISTICS IO和SET STATISTICS TIME 优化SQL Server查询性能
首先需要说明的是这篇文章的内容并不是如何调节SQL Server查询性能的(有关这方面的内容能写一本书),而是如何在SQL Server查询性能的调节中利用SET STATISTICS IO和SET ...