玩转单元測试之WireMock -- Web服务模拟器

WireMock 是一个灵活的库用于 Web 服务測试,和其它測试工具不同的是。WireMock 创建一个实际的 HTTPserver来执行你的 Web 服务以方便測试。

它支持 HTTP 响应存根、请求验证、代理/拦截、记录和回放。 而且能够在单元測试下使用或者部署到測试环境。

它能够用在哪些场景下:

  • 測试移动应用依赖于第三方REST APIs
  • 创建高速原型的APIs
  • 注入否则难于模拟第三方服务中的错误
  • 不论什么单元測试的代码依赖于web服务的

文件夹
   前提条件
   Maven配置
   准备工作
   Examples
   Troubleshooting
   參考

前提条件


  • JDK 1.7
  • Maven 3

Maven配置



pom里加入下面的dependencies

<dependency>
      <groupId>com.github.tomakehurst</groupId>
      <artifactId>wiremock</artifactId>
      <version>1.53</version>
      <classifier>standalone</classifier>
</dependency>

<dependency>

<groupId>org.testng</groupId>

<artifactId>testng</artifactId>

<version>6.8</version>

</dependency>

假设有依赖冲突,能够exclued 掉冲突的依赖, 配置例如以下

<dependency>
    <groupId>com.github.tomakehurst</groupId>
    <artifactId>wiremock</artifactId>
    <version>1.53</version>

    <!-- Include everything below here if you have dependency conflicts -->
    <classifier>standalone</classifier>
    <exclusions>
        <exclusion>
          <groupId>org.mortbay.jetty</groupId>
          <artifactId>jetty</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.google.guava</groupId>
          <artifactId>guava</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-core</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-annotations</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.fasterxml.jackson.core</groupId>
          <artifactId>jackson-databind</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.apache.httpcomponents</groupId>
          <artifactId>httpclient</artifactId>
        </exclusion>
        <exclusion>
          <groupId>org.skyscreamer</groupId>
          <artifactId>jsonassert</artifactId>
        </exclusion>
        <exclusion>
          <groupId>xmlunit</groupId>
          <artifactId>xmlunit</artifactId>
        </exclusion>
        <exclusion>
          <groupId>com.jayway.jsonpath</groupId>
          <artifactId>json-path</artifactId>
        </exclusion>
        <exclusion>
          <groupId>net.sf.jopt-simple</groupId>
          <artifactId>jopt-simple</artifactId>
        </exclusion>
     </exclusions>
</dependency>

准备工作



首先我写了一个类HTTPRequestor用来运行Http request訪问Rest服务的。 然后我须要一个Rest服务来測试我写的类是否ok, 但我手上没有一个真实的Rest web service, 所以WireMock就能够出场了,模拟一个Rest web serivce来測试我这个类。

HTTPRequestor例如以下:

 1 package com.demo.HttpRequestor;
 2
 3 import static com.jayway.restassured.RestAssured.given;
 4
 5 import java.util.HashMap;
 6 import java.util.Map;
 7
 8 import org.slf4j.Logger;
 9 import org.slf4j.LoggerFactory;
10
11 import com.jayway.restassured.response.Response;
12 import com.jayway.restassured.specification.RequestSpecification;
13
14 /**
15  * Wrapper for RestAssured. Perform an HTTP requests.
16  *
17  * @author wadexu
18  *
19  */
20 public class HTTPRequestor {
21
22         protected static final Logger logger = LoggerFactory.getLogger(HTTPRequestor.class);
23     private RequestSpecification reqSpec;
24
25
26     /**
27      * Constructor. Initializes the RequestSpecification (relaxedHTTPSValidation
28      * avoids certificate errors).
29      *
30      */
31     public HTTPRequestor() {
32         reqSpec = given().relaxedHTTPSValidation();
33     }
34
35     public HTTPRequestor(String proxy) {
36         reqSpec = given().relaxedHTTPSValidation().proxy(proxy);
37     }
38
39     /**
40      * Performs the request using the stored request data and then returns the response
41      *
42      * @param url
43      * @param method
44      * @param headers
45      * @param body
46      * @return response Response, will contain entire response (response string and status code).
47      * @throws Exception
48      */
49     public Response perform_request(String url, String method, HashMap<String, String> headers, String body) throws Exception {
50
51         Response response = null;
52
53         try {
54
55           for(Map.Entry<String, String> entry: headers.entrySet()) {
56             reqSpec.header(entry.getKey(), entry.getValue());
57           }
58
59           switch(method) {
60
61             case "GET": {
62               response = reqSpec.get(url);
63               break;
64             }
65             case "POST": {
66               response = reqSpec.body(body).post(url);
67               break;
68             }
69             case "PUT": {
70               response = reqSpec.body(body).put(url);
71               break;
72             }
73             case "DELETE": {
74               response = reqSpec.delete(url);
75               break;
76             }
77
78             default: {
79               logger.error("Unknown call type: [" + method + "]");
80             }
81           }
82
83         } catch (Exception e) {
84           logger.error("Problem performing request: ", e);
85         }
86
87         return response;
88       }
89 }

这个类是须要依赖 jayway 的 rest-assured包的

        <dependency>
            <groupId>com.jayway.restassured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>2.3.3</version>
            <scope>test</scope>
        </dependency>

Examples



新建一个測试类HTTPRequestorMockTest

new 一个 WireMockService 配置一下 然后启动

        wireMockServer = new WireMockServer(wireMockConfig().port(8090));
        WireMock.configureFor("localhost", 8090);
        wireMockServer.start(); 

在測试方法之前

创建存根, 指明是GET方法,URL路径, Header的内容,会返回什么样的Response

    @BeforeTest
    public void stubRequests() {
      stubFor(get(urlEqualTo("/cars/Chevy"))
              .withHeader("Accept", equalTo("application/json"))
              .withHeader("User-Agent", equalTo("Jakarta Commons-HttpClient/3.1"))
                      .willReturn(aResponse()
                                  .withHeader("content-type", "application/json")
                                  .withStatus(200)
                                  .withBody("{\"message\":\"Chevy car response body\"}")
                                 )
             );
    }

##转载注明出处: http://www.cnblogs.com/wade-xu/p/4299710.html

一切都模拟好了,接下来開始測试了。測试方法例如以下

@Test
    public void test_Get_Method() {

        String url = "http://localhost:8090/cars/Chevy";
        String method = "GET";
        String body = "";

        HashMap<String, String> headers = new HashMap<String, String>();
        headers.put("Accept", "application/json");
        headers.put("User-Agent", "Jakarta Commons-HttpClient/3.1");

        HTTPRequestor httpRequestor = new HTTPRequestor();
        Response response = null;

        try {
                response = httpRequestor.perform_request(url, method, headers, body);
        } catch (Exception e) {
                fail("Problem using HTTPRequestor to generate response: " + e.getMessage());
        }

        assertEquals(200, response.getStatusCode());
        assertEquals("Chevy car response body", response.jsonPath().get("message"));

    }

上面的样例是GET,没有请求体。以下我们来看POST的样例

同理 创建存根

RequestBody如果为"Mini Cooper"

 stubFor(post(urlEqualTo("/cars/Mini"))
              .withHeader("Authorization", equalTo("Basic d8d74jf82o929d"))
              .withHeader("Accept", equalTo("application/json"))
              .withHeader("User-Agent", equalTo("Jakarta Commons-HttpClient/3.1"))
              .withRequestBody(equalTo("Mini Cooper"))
                      .willReturn(aResponse()
                                  .withHeader("content-type", "application/json")
                                  .withStatus(200)
                                  .withBody("{\"message\":\"Mini Cooper car response body\", \"success\":true}")
                                 )
             );

測试方法例如以下:

 @Test
    public void test_Post_Method() {

        String url = "http://localhost:8090/cars/Mini";
        String method = "POST";
        String body = "Mini Cooper";

        HashMap<String, String> headers = new HashMap<String, String>();
        headers.put("Authorization", "Basic d8d74jf82o929d");
        headers.put("Accept", "application/json");
        headers.put("User-Agent", "Jakarta Commons-HttpClient/3.1");

        HTTPRequestor httpRequestor = new HTTPRequestor();
        Response response = null;

        try {
                response = httpRequestor.perform_request(url, method, headers, body);
        } catch (Exception e) {
                fail("Problem using HTTPRequestor to generate response: " + e.getMessage());
        }

        assertEquals(200, response.getStatusCode());
        assertEquals("Mini Cooper car response body", response.jsonPath().get("message"));
        assertEquals(true, response.jsonPath().get("success"));

    }

PUT 和 DELETE 都是一样的道理,有兴趣的读者能够自行练习。

測试结束之后 不要忘记tear down, 停掉WireMockServer

@AfterTest(alwaysRun=true)
    public void tearDown() {
      wireMockServer.stop();
      wireMockServer.shutdown();
    }

贴出我的整个測试类 (两个測试方法都须要相同的參数,所以能够用@DataProvider的方式来改进。我这里就不具体阐述了)

1 package com.demo.mocktest;
  2
  3 import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
  4 import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
  5 import static com.github.tomakehurst.wiremock.client.WireMock.get;
  6 import static com.github.tomakehurst.wiremock.client.WireMock.post;
  7 import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
  8 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
  9 import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
 10 import static org.testng.Assert.assertEquals;
 11 import static org.testng.Assert.fail;
 12
 13 import java.util.HashMap;
 14
 15 import org.testng.ITest;
 16 import org.testng.annotations.AfterTest;
 17 import org.testng.annotations.BeforeTest;
 18 import org.testng.annotations.Test;
 19
 20 import com.demo.HttpRequestor.HTTPRequestor;
 21 import com.github.tomakehurst.wiremock.WireMockServer;
 22 import com.github.tomakehurst.wiremock.client.WireMock;
 23 import com.jayway.restassured.response.Response;
 24
 25 public class HTTPRequestorMockTest implements ITest{
 26
 27     private WireMockServer wireMockServer;
 28
 29     @Override
 30     public String getTestName() {
 31       return "Mock Test";
 32     }
 33
 34     public HTTPRequestorMockTest() {
 35         wireMockServer = new WireMockServer(wireMockConfig().port(8090));
 36         WireMock.configureFor("localhost", 8090);
 37         wireMockServer.start();
 38     }
 39
 40     @BeforeTest
 41     public void stubRequests() {
 42       stubFor(get(urlEqualTo("/cars/Chevy"))
 43               .withHeader("Accept", equalTo("application/json"))
 44               .withHeader("User-Agent", equalTo("Jakarta Commons-HttpClient/3.1"))
 45                       .willReturn(aResponse()
 46                                   .withHeader("content-type", "application/json")
 47                                   .withStatus(200)
 48                                   .withBody("{\"message\":\"Chevy car response body\"}")
 49                                  )
 50              );
 51
 52       stubFor(post(urlEqualTo("/cars/Mini"))
 53               .withHeader("Authorization", equalTo("Basic d8d74jf82o929d"))
 54               .withHeader("Accept", equalTo("application/json"))
 55               .withHeader("User-Agent", equalTo("Jakarta Commons-HttpClient/3.1"))
 56               .withRequestBody(equalTo("Mini Cooper"))
 57                       .willReturn(aResponse()
 58                                   .withHeader("content-type", "application/json")
 59                                   .withStatus(200)
 60                                   .withBody("{\"message\":\"Mini Cooper car response body\", \"success\":true}")
 61                                  )
 62              );
 63     }
 64
 65     @Test
 66     public void test_Get_Method() {
 67
 68         String url = "http://localhost:8090/cars/Chevy";
 69         String method = "GET";
 70         String body = "";
 71
 72         HashMap<String, String> headers = new HashMap<String, String>();
 73         headers.put("Accept", "application/json");
 74         headers.put("User-Agent", "Jakarta Commons-HttpClient/3.1");
 75
 76
 77         HTTPRequestor httpRequestor = new HTTPRequestor();
 78         Response response = null;
 79
 80         try {
 81                 response = httpRequestor.perform_request(url, method, headers, body);
 82         } catch (Exception e) {
 83                 fail("Problem using HTTPRequestor to generate response: " + e.getMessage());
 84         }
 85
 86         assertEquals(200, response.getStatusCode());
 87         assertEquals("Chevy car response body", response.jsonPath().get("message"));
 88
 89     }
 90
 91     @Test
 92     public void test_Post_Method() {
 93
 94         String url = "http://localhost:8090/cars/Mini";
 95         String method = "POST";
 96         String body = "Mini Cooper";
 97
 98         HashMap<String, String> headers = new HashMap<String, String>();
 99         headers.put("Authorization", "Basic d8d74jf82o929d");
100         headers.put("Accept", "application/json");
101         headers.put("User-Agent", "Jakarta Commons-HttpClient/3.1");
102
103         HTTPRequestor httpRequestor = new HTTPRequestor();
104         Response response = null;
105
106         try {
107                 response = httpRequestor.perform_request(url, method, headers, body);
108         } catch (Exception e) {
109                 fail("Problem using HTTPRequestor to generate response: " + e.getMessage());
110         }
111
112         assertEquals(200, response.getStatusCode());
113         assertEquals("Mini Cooper car response body", response.jsonPath().get("message"));
114         assertEquals(true, response.jsonPath().get("success"));
115
116     }
117
118     @AfterTest(alwaysRun=true)
119     public void tearDown() {
120       wireMockServer.stop();
121       wireMockServer.shutdown();
122     }
123
124 }

##转载注明出处: http://www.cnblogs.com/wade-xu/p/4299710.html

Run as TestNG

測试结果例如以下:

PASSED: Mock Test
PASSED: Mock Test

===============================================
    Default test
    Tests run: 2, Failures: 0, Skips: 0
===============================================

[TestNG] Time taken by [email protected]: 7 ms
[TestNG] Time taken by [TestListenerAdapter] Passed:0 Failed:0 Skipped:0]: 1 ms
[TestNG] Time taken by [email protected]: 5 ms
[TestNG] Time taken by [email protected]: 7 ms
[TestNG] Time taken by [email protected]: 31 ms

Troubleshooting



HTTPRequestor类第59行 报错Cannot switch on a value of type String for source level below 1.7. Only convertible int values or enum constants are permitted

--- Java Build Path 设置 JRE System library 1.7 以上

Static import 例如以下:

import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.post;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

參考



官方文档:http://wiremock.org/

本文同一时候发表在博客园 http://www.cnblogs.com/wade-xu/p/4299710.html

时间: 2024-08-05 10:49:39

玩转单元測试之WireMock -- Web服务模拟器的相关文章

玩转单元测试之WireMock -- Web服务模拟器

玩转单元测试之WireMock -- Web服务模拟器 WireMock 是一个灵活的库用于 Web 服务测试,和其他测试工具不同的是,WireMock 创建一个实际的 HTTP服务器来运行你的 Web 服务以方便测试. 它支持 HTTP 响应存根.请求验证.代理/拦截.记录和回放, 并且可以在单元测试下使用或者部署到测试环境. 它可以用在哪些场景下: 测试移动应用依赖于第三方REST APIs 创建快速原型的APIs 注入否则难于模拟第三方服务中的错误 任何单元测试的代码依赖于web服务的 目

玩转单元測试之DBUnit

本文同一时候发表在:http://www.cnblogs.com/wade-xu/p/4547381.html DBunit 是一种扩展于JUnit的数据库驱动測试框架,它使数据库在測试过程之间处于一种已知状态.假设一个測试用例对数据库造成了破坏性影响,它能够帮助避免造成后面的測试失败或者给出错误结果. 尽管不是什么新奇货,但近期正好用到.就把学到的跟大家分享一下. 关键词:数据库层測试,DAO层測试,DBUnit教程,DBUnit入门.DBUnit实例,Sring中结合DBUnit对Dao层測

[iOS翻译]《iOS7 by Tutorials》在Xcode 5里使用单元測试(上)

简单介绍: 单元測试是软件开发的一个重要方面.毕竟,单元測试能够帮你找到bug和崩溃原因,而程序崩溃是Apple在审查时拒绝app上架的首要原因. 单元測试不是万能的,但Apple把它作为开发工具包的一部分,不仅让你创作的APP更稳定,并且提供了一致.有趣的用户体验,这些都是让用户给你五星评价的源泉.iOS7提供了一个升级的单元測试框架.让你在Xcode中执行单元測试更为easy.当你完毕这一章节,你将学会怎样给现有app加入測试--并有可能培养出对编写測试的热爱! /* 本文翻译自<iOS7

C语言单元測试

对于敏捷开发来说,单元測试不可缺少,对于Java开发来说,JUnit非常好,对于C++开发,也有CPPUnit可供使用,而对于传统的C语言开发,就没有非常好的工具可供使用,能够找到的有这么几个工具: CuTest -- CuTest(Cute Test)是一个很easy的C语言单元測试工具.在使用它的时候,仅仅须要包括两个文件“CuTest.c CuTest.h”,然后就能够写測试用例,进行測试了.它对用例差点儿没有管理功能,报表输出也很easy,能够用来试验单元測试的基本想法. CUnit -

Android 进行单元測试难在哪-part3

原文链接 : HOW TO MAKE OUR ANDROID APPS UNIT TESTABLE (PT. 1) 原文作者 : Matthew Dupree 译文出自 : 开发技术前线 www.devtf.cn 译者 : chaossss 校对者: tiiime 状态 : 完毕 在 Android 应用中进行单元測试非常困难.有时候甚至是不可能的.在之前的两篇博文中,我已经向大家解释了在 Android 中进行单元測试如此困难的原因.而上一篇博文我们通过分析得到的结论是:正是 Google 官

利用Continuous Testing实现Eclipse环境自己主动单元測试

当你Eclipse环境中改动项目中的某个方法时,你可能因为各种原因没有执行单元測试,结果代码提交,悲剧就可能随之而来. 所幸infinitest(http://infinitest.github.io/)提供了一个Continuous Testing插件,以及时自己主动执行单元測试.尽管会多占一些CPU资源,但开发者的硬件谁会不留一点余地呢?大不了,音乐.视频.360卸载就OK了.安装方法有两种: (1)使用"Install new software",输入地址:http://infi

在Eclipse中使用JUnit4进行单元測试(0基础篇)

本文绝大部分内容引自这篇文章: http://www.devx.com/Java/Article/31983/0/page/1 我们在编写大型程序的时候,须要写成千上万个方法或函数,这些函数的功能可能非常强大,但我们在程序中仅仅用到该函数的一小部分功能,而且经过调试能够确定,这一小部分功能是正确的.可是,我们同一时候应该确保每个函数都全然正确,由于假设我们今后假设对程序进行扩展,用到了某个函数的其它功能,而这个功能有bug的话,那绝对是一件非常郁闷的事情.所以说,每编写完一个函数之后,都应该对这

新手学測试----Unit Test(单元測试)

在程序猿做项目的过程中,每当完毕一个功能,首先自己须要对完毕的功能进行測试.我如今正在做的项目用的工具是VS2012.那么接下来,就说一说在VS2012中是怎样创建单元測试的. 怎样创建单元測试? 在VS2012中,右键类名默认是没有创建单元測试的选项的,得须要设置加入.工具-->自己定义: 然后选择命令-->上下文菜单-->编辑器上下文菜单|代码窗体.然后找到创建单元測试,并将其上下移动到下图所看到的位置: 然后又一次打开VS.在类名上右键就能够看到灰色的创建单元測试,处于禁用状态:

单元測试的优点

对于单元測试.我慢慢的用得多起来.前不久.还对这个东西朦朦胧胧,认为非常神奇. 如今,我认为单元測试真是极好的. 好在哪里呢?就是好来就是好! 靠,这又不是某D某主义,得讲理.怎么个好法,要说出理由. 好吧.我认为单元測试能够 1.保证代码质量 2.提高开发效率 比方说,这2天我与还有一位同事共同开发某模块.他搞前端,我写server端.他要调用我的方法. 开发是并行的,我在写方法的时候,他的界面还没好,那怎么确保我的方法正确呢?不可能等他写好界面,写好调用我方法的代码,然后我俩再一起測试吧?