大家是如何对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的一种单元测试方案的更多相关文章

  1. WebApi的一种集成测试写法(in-memory)

    WebApi的一种集成测试写法(in-memory)   大家是如何对webApi写测试的呢? 1.利用Fiddler直接做请求,观察response的内容. 2.利用Httpclient做请求,断言 ...

  2. 总结:视频播放的四种实现方案(Native)

    一.来自 AVFoundation的 AVPlayer对象 特点: 1. AVPlayer     > 优点:          可以自定义UI, 进行控制     > 缺点:      ...

  3. 转【实战体验几种MySQLCluster方案】

    实战体验几种MySQLCluster方案 1.背景 MySQL的cluster方案有很多官方和第三方的选择,选择多就是一种烦恼,因此,我们考虑MySQL数据库满足下三点需求,考察市面上可行的解决方案: ...

  4. 真正意义上的spring环境中的单元测试方案spring-test与mokito完美结合

    真正意义上的spring环境中的单元测试方案spring-test与mokito完美结合 博客分类: java 测试 单元测试SpringCC++C#  一.要解决的问题:     spring环境中 ...

  5. 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案

    一. 简介 揭秘: SimpleThreadPool是Quartz.Net中自带的线程池,默认个数为10个,代表一个Scheduler同一时刻并发的最多只能执行10个job,超过10个的job需要排队 ...

  6. [转CSDN多篇文章]WEB 3D SVG CAD 矢量 几种实现方案

    WEB 3D SVG CAD 矢量 几种实现方案 原创 2014年10月24日 08:34:11 标签: WEB3D / CADSVG / 矢量 2665 一.全部自己开发,从底层开始 VML+SVG ...

  7. 分布式唯一ID的几种生成方案

    前言 在互联网的业务系统中,涉及到各种各样的ID,如在支付系统中就会有支付ID.退款ID等.那一般生成ID都有哪些解决方案呢?特别是在复杂的分布式系统业务场景中,我们应该采用哪种适合自己的解决方案是十 ...

  8. 第九节: 利用RemoteScheduler实现Sheduler的远程控制 第八节: Quartz.Net五大构件之SimpleThreadPool及其四种配置方案 第六节: 六类Calander处理六种不同的时间场景 第五节: Quartz.Net五大构件之Trigger的四大触发类 第三节: Quartz.Net五大构件之Scheduler(创建、封装、基本方法等)和Job(创建、关联

    第九节: 利用RemoteScheduler实现Sheduler的远程控制   一. RemoteScheduler远程控制 1. 背景: 在A服务器上部署了一个Scheduler,我们想在B服务器上 ...

  9. JavaScript常用八种继承方案

    更新:在常用七种继承方案的基础之上增加了ES6的类继承,所以现在变成八种啦,欢迎加高级前端进阶群一起学习(文末). --- 2018.10.30 1.原型链继承 构造函数.原型和实例之间的关系:每个构 ...

随机推荐

  1. ASCII、Unicode、GBK和UTF-8字符编码的区别联系

    转自http://dengo.org/archives/901 很久很久以前,有一群人,他们决定用8个可以开合的晶体管来组合成不同的状态,以表示世界上的万物.他们看到8个开关状态是好的,于是他们把这称 ...

  2. Python 字符串分割的方法

    在平时工作的时候,发现对于字符串分割的方法用的比较多,下面对分割字符串方法进行总结一下:第一种:split()函数split()函数应该说是分割字符串使用最多的函数用法:str.split('分割符' ...

  3. axure设置变量值

    以登录框为例设置axure变量值 1.打开axure,打开新页面命名为login,拖入一个矩形背景,命名:登录背景图 2.拖入标签控件和输入框控件分别命名为用户名:.userName.密码:.pass ...

  4. 即时通讯 TCP UDP

    TCP协议与UDP协议的区别    首先咱们弄清楚,TCP协议和UCP协议与TCP/IP协议的联系,很多人犯糊涂了,一直都是说TCP/IP协议与UDP协议的区别,我觉得这是没有从本质上弄清楚网络通信! ...

  5. interactivePopGestureRecognizer属性

    苹果一直都在人机交互中尽力做到极致,在iOS7中,新增加了一个小小的功能,也就是这个api:self.navigationController.interactivePopGestureRecogni ...

  6. 03-Swift常量&变量

    什么是常量和变量 在Swift中规定:在定义一个标识符时必须明确说明该标识符是一个常量还是变量 使用let来定义常量,定义之后不可以修改 使用var来定义变量,定义之后可以修改 常量和变量的基本使用 ...

  7. git使用--git命令项目提交问题总结

    提交遇到Error  "remote ref does not exist"解决办法:git fetch -p MY_REMOTE    eg.    git fetch -p o ...

  8. 总结30个CSS选择器

    或许大家平时总是在用的选择器都是:#id  .class  以及标签选择器.可是这些还远远不够,为了在开发中更加得心应手,本文总结了30个CSS3选择器,希望对大家有所帮助. 1 *:通用选择器 * ...

  9. Fire

    Fire 分析: 首先,明确题意:b1,b2,--,bn 交换为b2,--,bn,b1,但这并不是意味着只能从b1开始交换,(这点从样例中可以看出),并且也不意味着交换的必须是连续的一串,可以是几个单 ...

  10. python中zipfile文件名编码的问题

    在python中编程导入压缩包,利用zipfile包,从zipinfo读取文件名总是出错,创建的文件名是乱码,写入pgsql更是出错. 但在ubuntu下测试却正常,在windows下测试总是失败. ...