大家是如何对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. 发布以NLog作为日记工具的ASP.NET站点到IIS注意事项

    一.可以通过在Web.Config文件中添加节点来配置,或是直接将NLog.config放在Web.config所在目录 二.通过节点的fileName属性指定日志文件规则时,可以使用${basedi ...

  2. Java 基本数据类型(新手必看资料)

    变量就是申请内存来存储值.也就是说,当创建变量的时候,需要在内存中申请空间. 内存管理系统根据变量的类型为变量分配存储空间,分配的空间只能用来储存该类型数据. 因此,通过定义不同类型的变量,可以在内存 ...

  3. LINUX 如何开放端口和关闭端口/jps/sudo命令

    1 在java的根目录下用java的jps查看:============================================================================ ...

  4. Dreamweaver扩展注意事项

    对Dreamweaver扩展做了一些整理. 扩展开发扩展(Extension),是应用程序给用户预留的二次开发接口.Dreamweaver提供了对菜单,插入栏(Insertbar),浮动框等GUI部件 ...

  5. tab栏切换的特殊效果

    在实际的开发过程中,我们可能会遇到这种需求,如下图 左边是三个tab栏,右边是显示内容的div,当鼠标滑到坐标的tab上时,给它一个高亮显示,让它对应的内容在右边的div中显示出来,当鼠标移出的时候把 ...

  6. LeetCode(124) Binary Tree Maximum Path Sum

    题目 Given a binary tree, find the maximum path sum. For this problem, a path is defined as any sequen ...

  7. ActionBar

    Android actionBar 修改背景色 本来是想着自定义标题栏,发现老是出错.貌似4.0以上版本都不能自定义标题栏(我到现在都搞不清标题栏和actionBar的区别到底是什么!).原来需要自定 ...

  8. Java中的继承与静态static等的执行先后顺序

    package extend; public class X { Y y=new Y(); static{  System.out.println("tttt"); } X(){  ...

  9. 利用FPGA加速实现高性能计算

    原文链接 原因:处理器本身无法满足高性能计算(HPC)应用软件的性能需求,导致需求和性能 之间出现了缺口. 最初解决办法:使用协处理器来提升处理器的性能. 协处理器(基于硬件的设计)具有三种能力: 1 ...

  10. ListView之头部浮动效果

    ListView 中有时需要在顶部固定一个浮动栏,当向上滑动 ListView 时,浮动栏固定在顶部,当向下滑动 ListView 到其 HeaderView 可见时,浮动栏成为ListView的一部 ...