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()
{
    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请求的测试

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()
{
    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等方法

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()
        {
            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的一种集成测试写法(in-memory)的更多相关文章

  1. SQL Server 存储过程中处理多个查询条件的几种常见写法分析,我们该用那种写法

    本文出处: http://www.cnblogs.com/wy123/p/5958047.html 最近发现还有不少做开发的小伙伴,在写存储过程的时候,在参考已有的不同的写法时,往往很迷茫,不知道各种 ...

  2. 查询分页的几种Sql写法

    查询分页的几种Sql写法 摘自:http://www.cnblogs.com/zcttxs/archive/2012/04/01/2429151.html 1.创建测试环境,(插入100万条数据大概耗 ...

  3. 转--Android按钮单击事件的四种常用写法总结

    这篇文章主要介绍了Android按钮单击事件的四种常用写法总结,比较了常见的四种写法的优劣,有不错的参考借鉴价值,需要的朋友可以参考下     很多学习Android程序设计的人都会发现每个人对代码的 ...

  4. 代码片段--Makefile之大型工程项目子目录Makefile的一种通用写法

    转载:http://blog.csdn.net/mo_hui123456/article/details/8929615 管理Linux环境下的C/C++大型项目,如果有一个智能的Build Syst ...

  5. Makefile之大型工程项目子目录Makefile的一种通用写法

    管理Linux环境下的C/C++大型项目,如果有一个智能的Build System会起到事半功倍的效果,本文描述Linux环境下大型工程项目子目录Makefile的一种通用写法,使用该方法,当该子目录 ...

  6. Makefile之大型工程项目子目录Makefile的一种通用写法【转】

    转自:http://www.cnblogs.com/skyofbitbit/p/3680753.html 管理Linux环境下的C/C++大型项目,如果有一个智能的Build System会起到事半功 ...

  7. Android按钮单击事件的四种常用写法

    这篇文章主要介绍了Android按钮单击事件的四种常用写法总结,比较了常见的四种写法的优劣,有不错的参考借鉴价值,需要的朋友可以参考下 很多学习Android程序设计的人都会发现每个人对代码的写法都有 ...

  8. SQL Server 存储过程的几种常见写法分析,我们该用那种写法

    本文出处: http://www.cnblogs.com/wy123/p/5958047.html 最近发现还有不少做开发的小伙伴,在写存储过程的时候,在参考已有的不同的写法时,往往很迷茫,不知道各种 ...

  9. js中的三种函数写法

    js中的三种函数写法 <script type="text/javascript"> //普通的声明方式 function myFun(m,n){ alert(m+n) ...

随机推荐

  1. Zygote过程【3】——SystemServer诞生

    欢迎转载.转载请注明:http://blog.csdn.net/zhgxhuaa 在ZygoteInit的main()方法中做了几件大事.当中一件便是启动Systemserver进程.代码例如以下: ...

  2. Linux中查看socket状态(转)

    Linux中查看socket状态:cat /proc/net/sockstat #(这个是ipv4的) sockets: used 137 TCP: inuse 49 orphan 0 tw 3272 ...

  3. Java 开源博客 —— Solo 0.6.9 发布时间!

    Solo 它是 GitHub 上 Star 的最大数量 Java 博客系统,今天,我们宣布 0.6.9 正式版,欢迎来到下载. 特性 基于标签的文章分类 博客/标签 Atom/RSS.Sitemap ...

  4. 高榕资本宾悦:未使用的企业家Testin云测试服务类故障

    高榕资本岳斌:创业者未使用Testin云測试服务属不合格 2014/10/09 · Testin · 开发人员訪谈 Testin云測与工信部等联合承办的ICT中国.2014高层论坛之移动开发人员分论坛 ...

  5. 【C语言探索之旅】 第三部分第二课:SDL开发游戏之创建窗口和画布

    内容简介 1.第三部分第二课: SDL开发游戏之创建窗口和画布 2.第三部分第三课预告: SDL开发游戏之显示图像 第三部分第二课:SDL开发游戏之创建窗口和画布 在上一课中,我们对SDL这个开源库做 ...

  6. Java设计模式菜鸟系列(两)建模与观察者模式的实现

    转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39755577 观察者(Observer)模式定义:在对象之间定义了一对多的依赖关系,这样一 ...

  7. HDU 3831 DICS

    意甲冠军: 按标题给4操作模式  用最少的次数  离a串行转换b弦 思路: 因为操作仅仅有这4种  所以我们能够确定从头到位去匹配a和b一定是正确的 那么状态数一共同拥有多少呢  一共同拥有lengt ...

  8. RH133读书 笔记(4) - Lab 4 System Services

    Lab 4 System Services Goal: Develop skills using system administration tools and setting up and admi ...

  9. Linux tcpdump命令具体解释

    简单介绍 用简单的话来定义tcpdump,就是:dump the traffic on a network,依据使用者的定义对网络上的数据包进行截获的包分析工具. tcpdump能够将网络中传送的数据 ...

  10. STM32 水晶不摇

    刚刚得到一个新的董事会,该设备被编程为去,但执行地址不正确,没有进入c语言 没有进入c语言,有可能是一个难以回答的问题狗,硬狗拆除 检查以下四种情况 1.检查片内的功率是所有权利 2.检查晶体线短路 ...