玩转单元測试之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 org.testng.reporters.JUnitReportReporter@26b923ee: 7 ms
[TestNG] Time taken by [TestListenerAdapter] Passed:0 Failed:0 Skipped:0]: 1 ms
[TestNG] Time taken by org.testng.reporters.EmailableReporter@512f0124: 5 ms
[TestNG] Time taken by org.testng.reporters.XMLReporter@5a4ec51c: 7 ms
[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 例如以下:
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
玩转单元測试之WireMock -- Web服务模拟器的更多相关文章
- 玩转单元测试之WireMock -- Web服务模拟器
玩转单元测试之WireMock -- Web服务模拟器 WireMock 是一个灵活的库用于 Web 服务测试,和其他测试工具不同的是,WireMock 创建一个实际的 HTTP服务器来运行你的 We ...
- 玩转单元測试之DBUnit
本文同一时候发表在:http://www.cnblogs.com/wade-xu/p/4547381.html DBunit 是一种扩展于JUnit的数据库驱动測试框架,它使数据库在測试过程之间处于一 ...
- OpenStack中给wsgi程序写单元測试的方法
在 OpenStack 中, 针对web应用, 有三种方法来写单元測试 1) 使用webob生成模拟的request from __future__ import print_function imp ...
- 聊聊单元測试(一)——EasyMock
一.单元測试是保证软件质量的重要方法. 单元測试是对系统中某个模块功能的验证,但我们总会遇到这样那样的问题,导致測试代码非常难编写.最直接的一个原因便是强耦合关系,被測试者依赖一些不easy构造,比較 ...
- [iOS翻译]《iOS7 by Tutorials》在Xcode 5里使用单元測试(上)
简单介绍: 单元測试是软件开发的一个重要方面.毕竟,单元測试能够帮你找到bug和崩溃原因,而程序崩溃是Apple在审查时拒绝app上架的首要原因. 单元測试不是万能的,但Apple把它作为开发工具包的 ...
- 利用Continuous Testing实现Eclipse环境自己主动单元測试
当你Eclipse环境中改动项目中的某个方法时,你可能因为各种原因没有执行单元測试,结果代码提交,悲剧就可能随之而来. 所幸infinitest(http://infinitest.github.io ...
- 在Eclipse中使用JUnit4进行单元測试(0基础篇)
本文绝大部分内容引自这篇文章: http://www.devx.com/Java/Article/31983/0/page/1 我们在编写大型程序的时候,须要写成千上万个方法或函数,这些函数的功能可能 ...
- C语言单元測试
C语言单元測试 对于敏捷开发来说,单元測试不可缺少,对于Java开发来说,JUnit非常好,对于C++开发,也有CPPUnit可供使用,而对于传统的C语言开发,就没有非常好的工具可供使用,能够找到的有 ...
- Android单元測试之JUnit
随着近期几年測试方面的工作慢慢火热起来.常常看见有招聘測试project师的招聘信息.在Java中有单元測试这么一个JUnit 方式,Android眼下主要编写的语言是Java,所以在Android开 ...
随机推荐
- $("[lay-id='"+this.id+"']")
$("[lay-id='"+this.id+"']") $("[lay-id='"+this.id+"'] .layui-tabl ...
- php生成订单号-当天从1开始自增
/** * 生成订单号 * -当天从1开始自增 * -订单号模样:20190604000001 * @param Client $redis * @param $key * @param $back: ...
- incremental linking(增量链接)的作用
转:incremental linking(增量链接)的作用 今天编译一个C++程序时,报了一个奇怪的错误(之前是好好的): 1>LINK : fatal error LNK1123: fail ...
- 51nod 1057 n的阶乘 (压位优化)
题目地址:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1057&judgeId=605203 使用压位进行优化, ...
- JZOJ5776. 【NOIP2008模拟】小x游世界树
题目:[NOIP2008模拟]小x游世界树: 题目的附加题解给的很清楚,这里只给一个代码: #include<iostream> #include<cstdio> #inclu ...
- 大项目之网上书城(八)——数据库大改&添加图书
目录 大项目之网上书城(八)--数据库大改&添加图书 主要改动 1.数据库新增表 代码 2.数据库新增触发器 3.其他对BookService和BookDao的修改 代码 4.addBook. ...
- 解决mysql时区问题以及SSL问题
看了下网上的教程,觉得都太麻烦啦,这里推荐个简单的! 解决时区问题 只需要加上serverTimezone=UTC即可,如下: spring.datasource.url=jdbc:mysql://1 ...
- [Python3网络爬虫开发实战] 1.9.5-Scrapyrt的安装
Scrapyrt为Scrapy提供了一个调度的HTTP接口,有了它,我们就不需要再执行Scrapy命令而是通过请求一个HTTP接口来调度Scrapy任务了.Scrapyrt比Scrapyd更轻量,如果 ...
- 当执行计划中出现BITMAP CONVERSION TO ROWIDS关键字时,需要注意了。
前言 前些天优化了一些耗费buffers较多的SQL,但系统CPU降低的效果不明显,于是又拉了awr报告,查看了SQL ordered by Gets排名前列的SQL. 分析 SQL代码: selec ...
- stark组件之处理函数动态url构造(五)
在这个组件中有内置的4个处理函数,它们都有自己对应的url,那么它们的url是怎么构造的呢? ... urlpatterns = [ re_path('list/$', self.wrapper(se ...