如何在 Spock groovy 中模拟 RequestEntity.put() 和 restTemplate.exchange()
How to mock RequestEntity.put() and restTemplate.exchange() in Spock groovy
Java api 调用的代码
我想知道如何测试下面两行代码。
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();
}
这不是一个 Spock 问题(您没有提供一行 Spock 规范,所以到目前为止没有人知道您尝试了什么),而是一个软件工程或一般测试问题。测试意大利面条代码的问题——这里我指的是既做某事又同时创建大量对象的代码——是从外部无法访问在方法内创建并存储在局部变量中的对象。有两种方法可以重构代码以获得更好的可测试性:
如果有意义,请更改代码以使用户能够从外部注入依赖项,而不是代码在内部创建它们。请 google "dependency injection" 并找到类似
的变体
- 构造函数注入,
- setter注入,
- 方法参数注入,
- 像 CDI 一样使用 tools/paradigms 时的场注入。
另一种方法是将方法的对象创建部分分解为较小的生产者(或创建者或工厂)方法,然后您可以根据您在测试中的选择覆盖(存根)使用部分模拟(间谍)对象。 Spock提供了这样的间谍,所以你可以很容易地使用它们。
我将展示后一种方法供您参考:
package de.scrum_master.Whosebug.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);
}
}
看看该方法现在如何更加结构化和更具可读性(仍然可以改进,我只是以一种快速而肮脏的方式做到了)?请特别注意辅助方法 createRequestEntity
和 executeRequest
.
下面是编写 Spock 测试的方法:
package de.scrum_master.Whosebug.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"
}
}
如果您不理解这段代码,请随时提出(相关!)后续问题。我建议您学习更多关于干净代码、可测试性、一般模拟测试以及特别是 Spock 的知识。
Java api 调用的代码
我想知道如何测试下面两行代码。
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();
}
这不是一个 Spock 问题(您没有提供一行 Spock 规范,所以到目前为止没有人知道您尝试了什么),而是一个软件工程或一般测试问题。测试意大利面条代码的问题——这里我指的是既做某事又同时创建大量对象的代码——是从外部无法访问在方法内创建并存储在局部变量中的对象。有两种方法可以重构代码以获得更好的可测试性:
如果有意义,请更改代码以使用户能够从外部注入依赖项,而不是代码在内部创建它们。请 google "dependency injection" 并找到类似
的变体- 构造函数注入,
- setter注入,
- 方法参数注入,
- 像 CDI 一样使用 tools/paradigms 时的场注入。
另一种方法是将方法的对象创建部分分解为较小的生产者(或创建者或工厂)方法,然后您可以根据您在测试中的选择覆盖(存根)使用部分模拟(间谍)对象。 Spock提供了这样的间谍,所以你可以很容易地使用它们。
我将展示后一种方法供您参考:
package de.scrum_master.Whosebug.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);
}
}
看看该方法现在如何更加结构化和更具可读性(仍然可以改进,我只是以一种快速而肮脏的方式做到了)?请特别注意辅助方法 createRequestEntity
和 executeRequest
.
下面是编写 Spock 测试的方法:
package de.scrum_master.Whosebug.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"
}
}
如果您不理解这段代码,请随时提出(相关!)后续问题。我建议您学习更多关于干净代码、可测试性、一般模拟测试以及特别是 Spock 的知识。