玩转单元测试之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服务模拟器的更多相关文章

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

    WireMock 是一个灵活的库用于 Web 服务測试,和其它測试工具不同的是.WireMock 创建一个实际的 HTTPserver来执行你的 Web 服务以方便測试. 它支持 HTTP 响应存根. ...

  2. 玩转单元测试之Testing Spring MVC Controllers

    玩转单元测试之 Testing Spring MVC Controllers 转载注明出处:http://www.cnblogs.com/wade-xu/p/4311657.html The Spri ...

  3. IOS开发系列之阿堂教程:玩转IPhone客户端和Web服务端交互(客户端)实践

    说到ios的应用开发,我们不能不提到web server服务端,如果没有服务端的支持,ios应用开发就没有多大意义了,因为从事过手机开发的朋友都知道(Android也一样),大量复杂业务的处理和数据库 ...

  4. C#Unit单元测试之读取Web.config文件

    长期一来,我们所完成的项目都没有写单元测试,今天我一时兴起,决定给自己写的代码写单元测试,简单的测试代码分分钟完成了,一运行测试,就懵逼了.没能达到我的预期效果,而是出现图1所示错误. 图1:单元测试 ...

  5. 玩转单元测试之DBUnit

    DBunit 是一种扩展于JUnit的数据库驱动测试框架,它使数据库在测试过程之间处于一种已知状态,如果一个测试用例对数据库造成了破坏性影响,它可以帮助避免造成后面的测试失败或者给出错误结果. 虽然不 ...

  6. 无法在 Android 模拟器上访问本机的Web服务的解决办法

    我在本地跑了一个 Tomcat ,我想在 Android 模拟器中直接通过下面的 url 地址访问 Tomcat 上的服务 http://192.168.0.20:8080/getweather 但是 ...

  7. 如何在WP8模拟器中连接本地的web服务

    这个问题困扰了很久,查找答案一度找偏方向. 其实连接web服务对于wp7不是问题,因为wp7使用的网络就是本机的网络,但是到了wp8模拟器,纯粹的虚拟机,独立的设备,也就有了自己的网络连接,要当做虚拟 ...

  8. 补习系列(8)-springboot 单元测试之道

    目录 目标 一.About 单元测试 二.About Junit 三.SpringBoot-单元测试 项目依赖 测试样例 四.Mock测试 五.最后 目标 了解 单元测试的背景 了解如何 利用 spr ...

  9. 尝试利用CentOS环境安装LiteSpeed WEB服务环境的过程

    对于普通的网站搭建的环境虚拟主机就足够使用,不过近期公司的网站需要上线VPS主机,于是采用到LNMP(http://lnmp.org/)一键包安装的,运行还是比较好的,这不最近我也开始尝试接触VPS方 ...

随机推荐

  1. css属性选择器

    1.E[att] 选择具有attr属性的E元素.2.E[att="val"] 选择具有att属性且属性值等于val的E元素3.E[att~="val"] 选择具 ...

  2. AspNetPager分页

    1.页面部分 <%@ Register Assembly="AspNetPager" Namespace="Wuqi.Webdiyer" TagPrefi ...

  3. Android Studio 查看密钥库证书指纹SHA1

    打开DOC命令窗体

  4. 【Android】Android清除本地数据缓存代码

    最近做软件的时候,遇到了缓存的问题,在网上看到了这个文章,感觉不错.分享给大家看看 文章出处:http://www.cnblogs.com/rayray/p/3413673.html /* * 文 件 ...

  5. erlang中的lists:foldl()的用法,格式转换实例应用

    lists:foldl(fun(),参数1,参数2):这个函数就是先把参数1传给fun()处理,然后将参数2(列表)中每一个元素,依次传给fun()函数进行处理. lists:foldl(fun(El ...

  6. Hadoop数据类型介绍

    我们知道hadoop是由Java 编程写的.因此我们使用Java开发环境来操作HDFS,编写mapreduce也是很自然的事情.但是这里面hadoop却对Java数据类型进行了包装,那么hadoop的 ...

  7. C++虚函数示例

    和Java不同,CDerive derive语句可以直接生成对象,不需要new关键字 重载虚函数才可以用父类引用调用子类对象,重载普通函数没有效果 #include<iostream> # ...

  8. POJ 2155 2维线段树 || 2维BIT

    #include <iostream> #include <cstring> #include <cstdio> #include <algorithm> ...

  9. C#线性表之顺序表

    线性表是最简单.最基本.最常用的数据结构.线性表是线性结构的抽象(Abstract), 线性结构的特点是结构中的数据元素之间存在一对一的线性关系. 这种一对一的关系指的是数据元素之间的位置关系,即: ...

  10. 在 Xcode 6 中使用矢量图( iPhone 6 置配 UI)

    在 Xcode 6 中使用矢量图( iPhone 6 置配 UI) (本文转载:http://iosdeveloper.diandian.com/post/2014-09-25/40063062789 ...