大家是如何对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. public class OrderController : ApiController
  2. {
  3. // GET api/order
  4. public Order Get()
  5. {
  6. return new Order(){Id = 1,Descriptions = "descriptions",Name = "name"};
  7. }
  8.  
  9. // GET api/order/5
  10. public string Get(int id)
  11. {
  12. return "value";
  13. }
  14.  
  15. // POST api/order
  16. public Order Post(Order order)
  17. {
  18. return order;
  19. }
  20.  
  21. // DELETE api/order/5
  22. public void Delete(int id)
  23. {
  24. }
  25. }

二、WebApi的请求过程

webApi的核心是对消息的管道处理,整个核心是有一系列消息处理器(HttpMessageHandler)首尾连接的双向管道,管道头为HttpServer,管道尾为HttpControllerDispatcher,HttpControllerDispatcher负责对controller的激活和action的执行,然后相应的消息逆向流出管道。

所以我们可以利用HttpMessageInvoker将一个请求消息HttpRequestMessage发送到管道中,最后收到的消息HttpResponseMessage就代表一个真实的请求响应。

三、Get请求的测试

  1. [Test]
  2. public void GetTest()
  3. {
  4. string baseAddress = "http://localhost:33203/";
  5.  
  6. HttpConfiguration config = new HttpConfiguration();
  7. WebApiConfig.Register(config);
  8. config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
  9. HttpServer server = new HttpServer(config);
  10. HttpMessageInvoker messageInvoker = new HttpMessageInvoker(server);
  11. CancellationTokenSource cts = new CancellationTokenSource();
  12. HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, baseAddress + "api/order");
  13.  
  14. using (HttpResponseMessage response = messageInvoker.SendAsync(request, cts.Token).Result)
  15. {
  16. var content = response.Content.ReadAsStringAsync().Result;
  17. var result = JsonConvert.DeserializeObject<Order>(content);
  18.  
  19. result.Name.Should().Be("name");
  20. }
  21. }

四、Post请求的测试

  1. [Test]
  2. public void PostTest()
  3. {
  4. string baseAddress = "http://localhost:33203/";
  5.  
  6. HttpConfiguration config = new HttpConfiguration();
  7. WebApiConfig.Register(config);
  8. config.IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always;
  9. HttpServer server = new HttpServer(config);
  10. HttpMessageInvoker messageInvoker = new HttpMessageInvoker(server);
  11. CancellationTokenSource cts = new CancellationTokenSource();
  12. HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Post, baseAddress + "api/order");
  13. var order = new Order() { Id = 1, Name = "orderName", Descriptions = "orderDescriptions" };
  14. request.Content = new ObjectContent<Order>(order, new JsonMediaTypeFormatter());
  15. using (HttpResponseMessage response = messageInvoker.SendAsync(request, cts.Token).Result)
  16. {
  17. var content = JsonConvert.SerializeObject(order, new JsonSerializerSettings() { ContractResolver = new CamelCasePropertyNamesContractResolver() });
  18. response.Content.ReadAsStringAsync().Result.Should().Be(content);
  19. }
  20. }

四、重构

可以看到这两个测试大部分的代码是相同的,都是用来发送请求。因此我们提取一个webApiTestBase类,该基类可以提供InvokeGetRequest,InvokePostRequest,InvokePutRequest等方法

  1. public abstract class ApiTestBase
  2. {
  3. public abstract string GetBaseAddress();
  4.  
  5. protected TResult InvokeGetRequest<TResult>(string api)
  6. {
  7. using (var invoker = CreateMessageInvoker())
  8. {
  9. using (var cts = new CancellationTokenSource())
  10. {
  11. var request = new HttpRequestMessage(HttpMethod.Get, GetBaseAddress() + api);
  12. using (HttpResponseMessage response = invoker.SendAsync(request, cts.Token).Result)
  13. {
  14. var result = response.Content.ReadAsStringAsync().Result;
  15. return JsonConvert.DeserializeObject<TResult>(result);
  16. }
  17. }
  18. }
  19. }
  20.  
  21. protected TResult InvokePostRequest<TResult, TArguemnt>(string api, TArguemnt arg)
  22. {
  23. var invoker = CreateMessageInvoker();
  24. using (var cts = new CancellationTokenSource())
  25. {
  26. var request = new HttpRequestMessage(HttpMethod.Post, GetBaseAddress() + api);
  27. request.Content = new ObjectContent<TArguemnt>(arg, new JsonMediaTypeFormatter());
  28. using (HttpResponseMessage response = invoker.SendAsync(request, cts.Token).Result)
  29. {
  30. var result = response.Content.ReadAsStringAsync().Result;
  31. return JsonConvert.DeserializeObject<TResult>(result);
  32. }
  33. }
  34. }
  35.  
  36. private HttpMessageInvoker CreateMessageInvoker()
  37. {
  38. var config = new HttpConfiguration();
  39. WebApiConfig.Register(config);
  40. var server = new HttpServer(config);
  41. var messageInvoker = new HttpMessageInvoker(server);
  42. return messageInvoker;
  43. }
  44. }

有了这个基类,我们写测试只需要重写方法GetBaseAddress(),然后直接调用基类方法并进行断言即可

  1. [TestFixture]
  2. public class OrderApiTests:ApiTestBase
  3. {
  4. public override string GetBaseAddress()
  5. {
  6. return "http://localhost:33203/";
  7. }
  8.  
  9. [Test]
  10. public void Should_get_order_successfully()
  11. {
  12. var result = InvokeGetRequest<Order>("api/order");
  13.  
  14. result.Name.Should().Be("name");
  15. result.Descriptions.Should().Be("descriptions");
  16. result.Id.Should().Be(1);
  17. }
  18.  
  19. [Test]
  20. public void Should_post_order_successfully()
  21. {
  22. var newOrder=new Order(){Name = "newOrder",Id = 100,Descriptions = "new-order-description"};
  23.  
  24. var result = InvokePostRequest<Order,Order>("api/order", newOrder);
  25.  
  26. result.Name.Should().Be("newOrder");
  27. result.Id.Should().Be(100);
  28. result.Descriptions.Should().Be("new-order-description");
  29. }
  30. }

是不是干净多了。

这种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. Codeforces 586D. Phillip and Trains 搜索

    D. Phillip and Trains time limit per test: 1 second memory limit per test :256 megabytes input: stan ...

  2. 影响google PageRank的因素

    1 与pr高的网站做链接: 2 内容质量高的网站链接 3 加入搜索引擎分类目录 4 加入免费开源目录 5 你的链接出现在流量大.知名度高.频繁更新的重要网站上 6 google对PDF格式的文件比较看 ...

  3. Error:SSL peer shut down incorrectly

    从别的地方拷贝过来的项目有时会报这个错误,解决方法 File -> Project Structure -> project 对比本地项目和拷贝项目并修改至与本地项目一致

  4. 【转载】PHP 开发者该知道的 5 个 Composer 小技巧

    Composer是新一代的PHP依赖管理工具.其介绍和基本用法可以看这篇<Composer PHP依赖管理的新时代>.本文介绍使用Composer的五个小技巧,希望能给你的PHP开发带来方 ...

  5. prism4 StockTrader RI 项目分析一些体会

    StockTrader RI 代码分析也有一段时间了 刚从codeplex获取到源代码的时候,看得一头雾水,不知所云(因为之前没做过wpf的项目,看文档也静不下那心来看) 后来就想了个笨办法,模拟项目 ...

  6. php特性包括哪些?

    PHP的特性包括: 1. PHP 独特的语法混合了 C.Java.Perl 以及 PHP 自创新的语法. 2. PHP可以比CGI或者Perl更快速的执行动态网页——动态页面方面,与其他的编程语言相比 ...

  7. sql server 2000数据库 最近经常出现某进程一直占用资源,阻塞?死锁?

    OA的数据库最近多次出现某进程一直占用资源,导致其他进程无法执行.使用sp_who2 和 sql server profiler跟踪查询,发现有以下几个语句常常占用资源: 1.declare @P1 ...

  8. checkbox标签已有checked=checked属性但是不显示勾选

    点击全选按钮,选中下面的列表,再次点击取消选择. 第一次的使用的方法是$("input[name=xxx]").attr('checked',true); 但是往往刷新页面第一次点 ...

  9. C# 将短时间格式变长正常时间格式

    DateTime dateTimeStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1));             ...

  10. [转] 利用SET STATISTICS IO和SET STATISTICS TIME 优化SQL Server查询性能

    首先需要说明的是这篇文章的内容并不是如何调节SQL Server查询性能的(有关这方面的内容能写一本书),而是如何在SQL Server查询性能的调节中利用SET STATISTICS IO和SET ...