玩转单元测试之WireMock -- Web服务模拟器
玩转单元测试之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>
准备工作
首先我写了一个类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