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

    准备工作


     首先我写了一个类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 }
    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 

  • 相关阅读:
    【转】利用MVC模式开发Java应用程序[组图]
    [转]JAVA三大框架SSH和MVC
    二进制转十进制
    MPlayerX For Mac白屏问题
    输入法切换设置
    【学习笔记】【C语言】选择结构-switch
    【学习笔记】【C语言】选择结构-if
    【学习笔记】【C语言】流程控制
    Mac OS X中开启或关闭显示隐藏文件
    Xcode6 模拟器不显示键盘
  • 原文地址:https://www.cnblogs.com/wade-xu/p/4299710.html
Copyright © 2011-2022 走看看