简体   繁体   English

如何在 Spock groovy 中模拟 RequestEntity.put() 和 restTemplate.exchange()

[英]How to mock RequestEntity.put() and restTemplate.exchange() in Spock groovy

Java code for api call Java 代码为 api 调用

I want to know how to test the below two lines of code.我想知道如何测试以下两行代码。

private void api(){
    //Code to call an API and i want to test this in groovy spock
    HttpHeaders httpHeaders = new HttpHeaders();      
    httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON); 
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    RestTemplate restTemplate = new RestTemplate();
    String url ="url";
    String body ="body";
    //How to mock below line
    RequestEntity<String> requestEntity = RequestEntity.put(new URI(url)).contentType(MediaType.APPLICATION_JSON).body(body);
    //And this line
    ResponseEntity<String> responseEntity = restTemplate.exchange(requestEntity,String.class);
    HttpStatus StatusCode = responseEntity.getStatusCode();
}

This is not a Spock question as such (you didn't present a single line of Spock specification, so nobody knows what you have tried so far), rather a software engineering or general testing question.这不是一个 Spock 问题本身(您没有提供一条 Spock 规范,所以到目前为止没人知道您尝试了什么),而是一个软件工程或一般测试问题。 The problem with testing spaghetti code - and here I mean code which both does something and creates lots of objects at the same time - is that from the outside there is no access to objects created inside a method and stored in a local variable.测试意大利面条代码的问题——这里我的意思是同时做某事并创建许多对象的代码——是从外部无法访问在方法内部创建并存储在局部变量中的对象。 There are two ways to refactor your code for better testability:有两种方法可以重构代码以获得更好的可测试性:

  • If it makes sense, change the code so as to enable the user to inject the dependencies from the outside instead of the code creating them internally.如果有意义,请更改代码以使用户能够从外部注入依赖项,而不是在内部创建它们的代码。 Please google "dependency injection" and find variants like请谷歌“依赖注入”并找到类似的变体

    • constructor injection,构造函数注入,
    • setter injection,二传手注入,
    • method parameter injection,方法参数注入,
    • field injection when using tools/paradigms like CDI.使用 CDI 等工具/范式时的现场注入。
  • Another way is to factor out the object-creating parts of the method into smaller producer (or creator or factory) methods which then you can overwrite (stub) according to your choice in your test when using a partial mock (spy) object.另一种方法是将方法的对象创建部分分解为较小的生产者(或创建者或工厂)方法,然后您可以在使用部分模拟(间谍)object 时根据您在测试中的选择覆盖(存根)。 Spock provides such spies, so you can easily use them. Spock 提供了这样的间谍,因此您可以轻松使用它们。

I am going to show the latter approach for your information:我将展示后一种方法以供您参考:

package de.scrum_master.stackoverflow.q58101434;

import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;

public class MyRestApi {
  public HttpStatus api() throws URISyntaxException {
    //Code to call an API and i want to test this in groovy spock
    HttpHeaders httpHeaders = createHttpHeaders();
    RestTemplate restTemplate = createRestTemplate();
    String url ="url";
    String body ="body";
    //How to mock below line
    RequestEntity<String> requestEntity = createRequestEntity(url, body);
    //And this line
    ResponseEntity<String> responseEntity = executeRequest(restTemplate, requestEntity);
    return responseEntity.getStatusCode();
  }

  HttpHeaders createHttpHeaders() {
    HttpHeaders httpHeaders = new HttpHeaders();
    httpHeaders.setAccept(Arrays.asList(MediaType.APPLICATION_JSON));
    httpHeaders.setContentType(MediaType.APPLICATION_JSON);
    return httpHeaders;
  }

  RestTemplate createRestTemplate() {
    return new RestTemplate();
  }

  RequestEntity<String> createRequestEntity(String url, String body) throws URISyntaxException {
    return RequestEntity.put(new URI(url)).contentType(MediaType.APPLICATION_JSON).body(body);
  }

  ResponseEntity<String> executeRequest(RestTemplate restTemplate, RequestEntity<String> requestEntity) {
    return restTemplate.exchange(requestEntity,String.class);
  }
}

See how the method is more structured and more readable now (could still be improved, I just did it in a quick & dirty way)?看看这个方法现在是如何更有条理和更具可读性(仍然可以改进,我只是以一种快速而肮脏的方式做到了)? Please especially notice the helper methods createRequestEntity and executeRequest .请特别注意辅助方法createRequestEntityexecuteRequest

Now here is how you can write your Spock test:现在,您可以编写 Spock 测试:

package de.scrum_master.stackoverflow.q58101434

import org.springframework.http.HttpStatus
import org.springframework.http.RequestEntity
import org.springframework.http.ResponseEntity
import spock.lang.Specification
import spock.lang.Unroll

class MyRestApiTest extends Specification {
  @Unroll
  def "API returns status code #statusCode"() {
    given: "prepare mocks + spy"
    RequestEntity<String> requestEntity = Mock()
    ResponseEntity<String> responseEntity = Mock() {
      getStatusCode() >> httpStatus
    }
    MyRestApi myRestApi = Spy() {
      createRequestEntity(_, _) >> requestEntity
      executeRequest(_, _) >> responseEntity
    }

    when: "execute API method"
    def result = myRestApi.api()

    then: "check expected results"
    // This actually only tests mockfunctionality, your real test would look differently
    statusCode == result.value()
    reasonPhrase == result.reasonPhrase

    where:
    httpStatus                   | statusCode | reasonPhrase
    HttpStatus.OK                | 200        | "OK"
    HttpStatus.MOVED_PERMANENTLY | 301        | "Moved Permanently"
    HttpStatus.UNAUTHORIZED      | 401        | "Unauthorized"
  }
}

Feel free to ask (related.) follow-up questions if you do not understand this code, I advise you to learn more about clean code, testability.如果您不了解此代码,请随时提出(相关)后续问题,我建议您了解有关干净代码,可测试性的更多信息。 mock testing in general and also about Spock in particular.一般的模拟测试,尤其是关于 Spock 的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM