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

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

它能够用在哪些场景下:

  • 測试移动应用依赖于第三方REST APIs
  • 创建高速原型的APIs
  • 注入否则难于模拟第三方服务中的错误
  • 不论什么单元測试的代码依赖于web服务的
  1. 文件夹
  2. 前提条件
  3. Maven配置
  4. 准备工作
  5. Examples
  6. Troubleshooting
  7. 參考

前提条件


  • JDK 1.7
  • Maven 3

Maven配置


pom里加入下面的dependencies

  1. <dependency>
  2. <groupId>com.github.tomakehurst</groupId>
  3. <artifactId>wiremock</artifactId>
  4. <version>1.53</version>
  5. <classifier>standalone</classifier>
  6. </dependency>

<dependency>

        <groupId>org.testng</groupId>

        <artifactId>testng</artifactId>

        <version>6.8</version>

  </dependency>

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

  1. <dependency>
  2. <groupId>com.github.tomakehurst</groupId>
  3. <artifactId>wiremock</artifactId>
  4. <version>1.53</version>
  5.  
  6. <!-- Include everything below here if you have dependency conflicts -->
  7. <classifier>standalone</classifier>
  8. <exclusions>
  9. <exclusion>
  10. <groupId>org.mortbay.jetty</groupId>
  11. <artifactId>jetty</artifactId>
  12. </exclusion>
  13. <exclusion>
  14. <groupId>com.google.guava</groupId>
  15. <artifactId>guava</artifactId>
  16. </exclusion>
  17. <exclusion>
  18. <groupId>com.fasterxml.jackson.core</groupId>
  19. <artifactId>jackson-core</artifactId>
  20. </exclusion>
  21. <exclusion>
  22. <groupId>com.fasterxml.jackson.core</groupId>
  23. <artifactId>jackson-annotations</artifactId>
  24. </exclusion>
  25. <exclusion>
  26. <groupId>com.fasterxml.jackson.core</groupId>
  27. <artifactId>jackson-databind</artifactId>
  28. </exclusion>
  29. <exclusion>
  30. <groupId>org.apache.httpcomponents</groupId>
  31. <artifactId>httpclient</artifactId>
  32. </exclusion>
  33. <exclusion>
  34. <groupId>org.skyscreamer</groupId>
  35. <artifactId>jsonassert</artifactId>
  36. </exclusion>
  37. <exclusion>
  38. <groupId>xmlunit</groupId>
  39. <artifactId>xmlunit</artifactId>
  40. </exclusion>
  41. <exclusion>
  42. <groupId>com.jayway.jsonpath</groupId>
  43. <artifactId>json-path</artifactId>
  44. </exclusion>
  45. <exclusion>
  46. <groupId>net.sf.jopt-simple</groupId>
  47. <artifactId>jopt-simple</artifactId>
  48. </exclusion>
  49. </exclusions>
  50. </dependency>

准备工作


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

HTTPRequestor例如以下:

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

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

  1. <dependency>
  2. <groupId>com.jayway.restassured</groupId>
  3. <artifactId>rest-assured</artifactId>
  4. <version>2.3.3</version>
  5. <scope>test</scope>
  6. </dependency>

Examples


新建一个測试类HTTPRequestorMockTest

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

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

在測试方法之前

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

  1. @BeforeTest
  2. public void stubRequests() {
  3. stubFor(get(urlEqualTo("/cars/Chevy"))
  4. .withHeader("Accept", equalTo("application/json"))
  5. .withHeader("User-Agent", equalTo("Jakarta Commons-HttpClient/3.1"))
  6. .willReturn(aResponse()
  7. .withHeader("content-type", "application/json")
  8. .withStatus(200)
  9. .withBody("{\"message\":\"Chevy car response body\"}")
  10. )
  11. );
  12. }

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

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

  1. @Test
  2. public void test_Get_Method() {
  3.  
  4. String url = "http://localhost:8090/cars/Chevy";
  5. String method = "GET";
  6. String body = "";
  7.  
  8. HashMap<String, String> headers = new HashMap<String, String>();
  9. headers.put("Accept", "application/json");
  10. headers.put("User-Agent", "Jakarta Commons-HttpClient/3.1");
  11.  
  12. HTTPRequestor httpRequestor = new HTTPRequestor();
  13. Response response = null;
  14.  
  15. try {
  16. response = httpRequestor.perform_request(url, method, headers, body);
  17. } catch (Exception e) {
  18. fail("Problem using HTTPRequestor to generate response: " + e.getMessage());
  19. }
  20.  
  21. assertEquals(200, response.getStatusCode());
  22. assertEquals("Chevy car response body", response.jsonPath().get("message"));
  23.  
  24. }

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

同理 创建存根

RequestBody如果为"Mini Cooper"

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

測试方法例如以下:

  1. @Test
  2. public void test_Post_Method() {
  3.  
  4. String url = "http://localhost:8090/cars/Mini";
  5. String method = "POST";
  6. String body = "Mini Cooper";
  7.  
  8. HashMap<String, String> headers = new HashMap<String, String>();
  9. headers.put("Authorization", "Basic d8d74jf82o929d");
  10. headers.put("Accept", "application/json");
  11. headers.put("User-Agent", "Jakarta Commons-HttpClient/3.1");
  12.  
  13. HTTPRequestor httpRequestor = new HTTPRequestor();
  14. Response response = null;
  15.  
  16. try {
  17. response = httpRequestor.perform_request(url, method, headers, body);
  18. } catch (Exception e) {
  19. fail("Problem using HTTPRequestor to generate response: " + e.getMessage());
  20. }
  21.  
  22. assertEquals(200, response.getStatusCode());
  23. assertEquals("Mini Cooper car response body", response.jsonPath().get("message"));
  24. assertEquals(true, response.jsonPath().get("success"));
  25.  
  26. }

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

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

  1. @AfterTest(alwaysRun=true)
  2. public void tearDown() {
  3. wireMockServer.stop();
  4. wireMockServer.shutdown();
  5. }

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

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

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

Run as TestNG

測试结果例如以下:

  1. PASSED: Mock Test
  2. PASSED: Mock Test
  3.  
  4. ===============================================
  5. Default test
  6. Tests run: 2, Failures: 0, Skips: 0
  7. ===============================================
  8.  
  9. [TestNG] Time taken by org.testng.reporters.JUnitReportReporter@26b923ee: 7 ms
  10. [TestNG] Time taken by [TestListenerAdapter] Passed:0 Failed:0 Skipped:0]: 1 ms
  11. [TestNG] Time taken by org.testng.reporters.EmailableReporter@512f0124: 5 ms
  12. [TestNG] Time taken by org.testng.reporters.XMLReporter@5a4ec51c: 7 ms
  13. [TestNG] Time taken by org.testng.reporters.SuiteHTMLReporter@5706937e: 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 例如以下:

  1. import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
  2. import static com.github.tomakehurst.wiremock.client.WireMock.equalTo;
  3. import static com.github.tomakehurst.wiremock.client.WireMock.get;
  4. import static com.github.tomakehurst.wiremock.client.WireMock.post;
  5. import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
  6. import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
  7. import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;

參考


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

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

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

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

    玩转单元测试之WireMock -- Web服务模拟器 WireMock 是一个灵活的库用于 Web 服务测试,和其他测试工具不同的是,WireMock 创建一个实际的 HTTP服务器来运行你的 We ...

  2. 玩转单元測试之DBUnit

    本文同一时候发表在:http://www.cnblogs.com/wade-xu/p/4547381.html DBunit 是一种扩展于JUnit的数据库驱动測试框架,它使数据库在測试过程之间处于一 ...

  3. OpenStack中给wsgi程序写单元測试的方法

    在 OpenStack 中, 针对web应用, 有三种方法来写单元測试 1) 使用webob生成模拟的request from __future__ import print_function imp ...

  4. 聊聊单元測试(一)——EasyMock

    一.单元測试是保证软件质量的重要方法. 单元測试是对系统中某个模块功能的验证,但我们总会遇到这样那样的问题,导致測试代码非常难编写.最直接的一个原因便是强耦合关系,被測试者依赖一些不easy构造,比較 ...

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

    简单介绍: 单元測试是软件开发的一个重要方面.毕竟,单元測试能够帮你找到bug和崩溃原因,而程序崩溃是Apple在审查时拒绝app上架的首要原因. 单元測试不是万能的,但Apple把它作为开发工具包的 ...

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

    当你Eclipse环境中改动项目中的某个方法时,你可能因为各种原因没有执行单元測试,结果代码提交,悲剧就可能随之而来. 所幸infinitest(http://infinitest.github.io ...

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

    本文绝大部分内容引自这篇文章: http://www.devx.com/Java/Article/31983/0/page/1 我们在编写大型程序的时候,须要写成千上万个方法或函数,这些函数的功能可能 ...

  8. C语言单元測试

    C语言单元測试 对于敏捷开发来说,单元測试不可缺少,对于Java开发来说,JUnit非常好,对于C++开发,也有CPPUnit可供使用,而对于传统的C语言开发,就没有非常好的工具可供使用,能够找到的有 ...

  9. Android单元測试之JUnit

    随着近期几年測试方面的工作慢慢火热起来.常常看见有招聘測试project师的招聘信息.在Java中有单元測试这么一个JUnit 方式,Android眼下主要编写的语言是Java,所以在Android开 ...

随机推荐

  1. 第一天 初识Python

    Python基础 一 编程语言     什么是编程语言?    上面提及的能够被计算机所识别的表达方式即编程语言,语言是沟通的介质,而编程语言是程序员与计算机沟通的介质.在编程的世界里,计算机更像是人 ...

  2. du查看文件大小

    du+文件名就可以查看文件大小 du+ -h + 文件名也是查看文件大小,只是-h会将文件大小转换成M,G等格式

  3. SQL Server错误: 0 解决方案

    1.已设置两种登录模式. 2.SQL Server配置管理器已配置好. 按Windows徽标键+R组合键,然后输入cmd. 再然后输入netsh winsock reset.接下来重启电脑,应该就可以 ...

  4. js 发送短信验证码倒计时

    html <input type="button" id="btn" value="免费获取验证码" onclick="se ...

  5. JavaSE-22 反射

    学习要点 反射概念 反射的应用 反射概述 1  反射机制 定义 Java反射机制是指在程序在运行状态中,动态获取信息以及动态调用对象方法的功能. Java反射的动态性质:运行时生成对象实例.运行期间调 ...

  6. JavaSE-16 集合框架

    学习要点 Java集合框架内容 ArrayList和LinkedList HashMap Iterator 泛型集合 Java的集合框架 1  概述 数据结构是以某种形式将数据组织在一起的集合,它不仅 ...

  7. 三、spring中高级装配(1)

    大概看了一下第三章的内容,我从项目中仔细寻找,始终没有发现哪里有这种配置,但是看完觉得spring还有这么牛B的功能啊,spring的厉害之处,这种设计程序的思想,很让我感慨... 一.环境与prof ...

  8. CentOS7-Git安装以及使用

    2018-09-14 Git安装 在bash终端中输入命令sudo yum install git回车. (出乎意料的顺利) 在随后出现的交互式对话中输入y即可. 随后,当任务执行完后,在bash中键 ...

  9. centos下安装redis(记录其中踩坑的过程)

    一.先下载到redis-3.0.4.tar.gz包(本文以3.0.4版本为例) 我将这个包放在/opt目录下,在/opt下并解压这个包 tar -zxvf redis-.tar.gz 然后进入redi ...

  10. 标量子查询中有ROWNUM=1怎么改?

    碰到标量子查询中有ROWNUM=1怎么改? select to_date(o.postdate,'yyyymmdd'), (select cur.c_code from cur_tbl cur whe ...