玩转单元测试之WireMock -- Web服务模拟器
WireMock 是一个灵活的库用于 Web 服务测试,和其他测试工具不同的是,WireMock 创建一个实际的 HTTP服务器来运行你的 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>
exclusions
准备工作
首先我写了一个类HTTPRequestor用来执行Http request访问Rest服务的, 然后我需要一个Rest服务来测试我写的类是否ok, 但我手上没有一个真实的Rest web service, 所以WireMock就可以出场了,模拟一个Rest web serivce来测试我这个类。
HTTPRequestor如下:
package com.demo.HttpRequestor; import static com.jayway.restassured.RestAssured.given; import java.util.HashMap;
import java.util.Map; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import com.jayway.restassured.response.Response;
import com.jayway.restassured.specification.RequestSpecification; /**
* Wrapper for RestAssured. Perform an HTTP requests.
*
* @author wadexu
*
*/
public class HTTPRequestor { protected static final Logger logger = LoggerFactory.getLogger(HTTPRequestor.class);
private RequestSpecification reqSpec; /**
* Constructor. Initializes the RequestSpecification (relaxedHTTPSValidation
* avoids certificate errors).
*
*/
public HTTPRequestor() {
reqSpec = given().relaxedHTTPSValidation();
} public HTTPRequestor(String proxy) {
reqSpec = given().relaxedHTTPSValidation().proxy(proxy);
} /**
* Performs the request using the stored request data and then returns the response
*
* @param url
* @param method
* @param headers
* @param body
* @return response Response, will contain entire response (response string and status code).
* @throws Exception
*/
public Response perform_request(String url, String method, HashMap<String, String> headers, String body) throws Exception { Response response = null; try { for(Map.Entry<String, String> entry: headers.entrySet()) {
reqSpec.header(entry.getKey(), entry.getValue());
} switch(method) { case "GET": {
response = reqSpec.get(url);
break;
}
case "POST": {
response = reqSpec.body(body).post(url);
break;
}
case "PUT": {
response = reqSpec.body(body).put(url);
break;
}
case "DELETE": {
response = reqSpec.delete(url);
break;
} default: {
logger.error("Unknown call type: [" + method + "]");
}
} } catch (Exception e) {
logger.error("Problem performing request: ", e);
} return response;
}
}
这个类是需要依赖 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的方式来改进,我这里就不详细阐述了)
package com.demo.mocktest; 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;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.fail; import java.util.HashMap; import org.testng.ITest;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test; import com.demo.HttpRequestor.HTTPRequestor;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.client.WireMock;
import com.jayway.restassured.response.Response; public class HTTPRequestorMockTest implements ITest{ private WireMockServer wireMockServer; @Override
public String getTestName() {
return "Mock Test";
} public HTTPRequestorMockTest() {
wireMockServer = new WireMockServer(wireMockConfig().port(8090));
WireMock.configureFor("localhost", 8090);
wireMockServer.start();
} @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\"}")
)
); 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_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")); } @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")); } @AfterTest(alwaysRun=true)
public void tearDown() {
wireMockServer.stop();
wireMockServer.shutdown();
} }
HTTPRequestorMockTest
##转载注明出处: 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 创建一个实际的 HTTPserver来执行你的 Web 服务以方便測试. 它支持 HTTP 响应存根. ...
-
玩转单元测试之Testing Spring MVC Controllers
玩转单元测试之 Testing Spring MVC Controllers 转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html The Spri ...
-
IOS开发系列之阿堂教程:玩转IPhone客户端和Web服务端交互(客户端)实践
说到ios的应用开发,我们不能不提到web server服务端,如果没有服务端的支持,ios应用开发就没有多大意义了,因为从事过手机开发的朋友都知道(Android也一样),大量复杂业务的处理和数据库 ...
-
C#Unit单元测试之读取Web.config文件
长期一来,我们所完成的项目都没有写单元测试,今天我一时兴起,决定给自己写的代码写单元测试,简单的测试代码分分钟完成了,一运行测试,就懵逼了.没能达到我的预期效果,而是出现图1所示错误. 图1:单元测试 ...
-
玩转单元测试之DBUnit
DBunit 是一种扩展于JUnit的数据库驱动测试框架,它使数据库在测试过程之间处于一种已知状态,如果一个测试用例对数据库造成了破坏性影响,它可以帮助避免造成后面的测试失败或者给出错误结果. 虽然不 ...
-
无法在 Android 模拟器*问本机的Web服务的解决办法
我在本地跑了一个 Tomcat ,我想在 Android 模拟器中直接通过下面的 url 地址访问 Tomcat 上的服务 http://192.168.0.20:8080/getweather 但是 ...
-
如何在WP8模拟器中连接本地的web服务
这个问题困扰了很久,查找答案一度找偏方向. 其实连接web服务对于wp7不是问题,因为wp7使用的网络就是本机的网络,但是到了wp8模拟器,纯粹的虚拟机,独立的设备,也就有了自己的网络连接,要当做虚拟 ...
-
补习系列(8)-springboot 单元测试之道
目录 目标 一.About 单元测试 二.About Junit 三.SpringBoot-单元测试 项目依赖 测试样例 四.Mock测试 五.最后 目标 了解 单元测试的背景 了解如何 利用 spr ...
-
尝试利用CentOS环境安装LiteSpeed WEB服务环境的过程
对于普通的网站搭建的环境虚拟主机就足够使用,不过近期公司的网站需要上线VPS主机,于是采用到LNMP(http://lnmp.org/)一键包安装的,运行还是比较好的,这不最近我也开始尝试接触VPS方 ...
随机推荐
-
Python-2 print
#1 print函数(python版本3.3.5): >>> help(print)Help on built-in function print in module builtin ...
-
读写ZIP&;JAR文件
1. ZipEntry 是包括目录的,也就是目录也被当做是一个单独的Entry,在列出它下面的文件之前先列出这个directory entry. 这个在解压ZIP文件的的时候特别有用,我们要先创建这个 ...
-
常见排序算法及其java实现
最近学习了下java,感觉java在基本语法上与C++非常相似.作为练习,我用java实现了冒泡排序.选择排序.插入排序.基尔排序.快速排序.堆排序.计数排序.合并排序. 以下为实现代码: publi ...
-
.NET笔试题(关于迭代的:遍历XML中的FileName)
一.使用迭代算法,遍历XML文件中所有的文件名 写一个函数返回文件名集合 在主函数中调用该方法: 以后有关迭代的都写到这儿
-
Base64加密解密
/// <summary> /// 实现Base64加密解密 /// </summary> public sealed class Base64 { /// <summa ...
-
angular中ui calendar的一些使用心得
ui calendar是封装fullcalendar的一款angular指令插件 官方地址:http://angular-ui.github.io/ui-calendar/ fullcalendar ...
-
【代码学习】GD库中简单的验证码
大体思路: 代码部分: <?php //1.创建画布 $img = imagecreatetruecolor(100,30); //2.设置颜色 值越小,颜色越深 $color1 = image ...
-
Java 实现的各种经典的排序算法小Demo
由于有上机作业,所以就对数据结构中常用的各种排序算法都写了个Demo,有如下几个: 直接插入排序 折半插入排序 希尔排序 冒泡排序 快速排序 选择排序 桶排序 Demo下载地址 下面谈一谈我对这几个排 ...
-
January 07th, 2018 Week 01st Sunday
To remember is to disengage from the present. 铭记过去就是放弃当下. To remember the past doesn't mean we would ...
-
JS_各种排序方法
排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存. 我们这里说说八大排序就是内部排序. 当n较大,则应采 ...