zoukankan      html  css  js  c++  java
  • 玩转单元測试之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


  • 相关阅读:
    日程管理APP测试用例
    日程管理APP的测试计划和测试矩阵
    Bug report——仿网易新闻APP
    NABCD模拟实验
    5w1h

    小组作业
    code review
    Mutual review
    阅读思考
  • 原文地址:https://www.cnblogs.com/slgkaifa/p/7137772.html
Copyright © 2011-2022 走看看