问题描述
我在为具有完全相同请求的多个响应设置 MockServerClient 时遇到问题。
我带着“时代”的期望读到了这可能会完成,但我无法使其适用于我的场景。
如果您使用此 JSON 调用服务(两次):
{
"id": 1
}
第一个响应应该是“passed true”,第二个“passed false”
响应 1:
{
"passed":true
}
响应 2:
{
"passed":false
}
我设置了第一个请求,但如何设置第二个请求?
import com.nice.project.MyService;
import com.nice.project.MyPojo;
import org.mockito.Mock;
import org.mockserver.integration.ClientAndServer;
import org.mockserver.matchers.TimetoLive;
import org.mockserver.matchers.Times;
import org.mockserver.model.Header;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBoottest;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.test.context.TestPropertySource;
import java.io.File;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import static org.assertj.core.api.Assertions.assertthat;
import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.Mockito.when;
import static org.mockserver.integration.ClientAndServer.startClientAndServer;
import static org.mockserver.model.HttpRequest.request;
import static org.mockserver.model.HttpResponse.response;
@SpringBoottest
public class Tests{
private static final int PORT = 9998;
private static ClientAndServer mockServer;
@Autowired
private MyService myService;
@BeforeAll
public void init(){
mockServer = startClientAndServer(PORT);
mockServer
.when(
request()
.withPath(testUrlValidateTransactionOk).withMethod(HttpMethod.POST.name())
.withHeaders(
new Header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON.toString())
)
.withBody(contains("\"id\":\"1\""))
).respond(
response().withStatusCode(HttpStatus.OK.value())
.withHeaders(
new Header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON.toString())
)
.withBody("{\"passed\":true}"));
// What do i set here? Or in the snippet before by chaining?
// mockServer.when()...
}
@Test
void t1{
//myService will internally call the MockServer
//FirsT CALL -> Pass
MyPojo p = myService.call(1);
assertthat(p.isPassed()).isEqualTo(Boolean.TRUE);
//SECOND CALL -> No Pass
MyPojo p2 = myService.call(1);
assertthat(p2.isPassed()).isEqualTo(Boolean.FALSE);
}
}
依赖项(相关):
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
</parent>
<groupId>com.nice.project</groupId>
<artifactId>testing</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test</name>
<description>Testing</description>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<httpclient.version>4.5.13</httpclient.version>
<mock-server.version>5.11.2</mock-server.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!--HTTP CLIENT-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<!--TEST-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-netty</artifactId>
<version>${mock-server.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mock-server</groupId>
<artifactId>mockserver-client-java</artifactId>
<version>${mock-server.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
提前致谢。
解决方法
您可以通过将 when/request/response 行为包装在一个方法中并多次调用它来创建一系列响应,如下所示:
private void whenValidateTransactionReturn(boolean isPassed) {
mockServer
.when(
request()
.withPath(testUrlValidateTransactionOk)
.withMethod(HttpMethod.POST.name())
.withHeaders(
new Header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON.toString()))
.withBody(contains("\"id\":\"1\"")))
.respond(
response()
.withStatusCode(HttpStatus.OK.value())
.withHeaders(
new Header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON.toString()))
.withBody("{\"passed\":" + isPassed + "}"));
}
然后你可以多次调用这个方法:
@Test
void testValidationFailsSecondTime() {
whenValidateTransactionReturn(true);
whenValidateTransactionReturn(false);
//
// Test logic
//
// mockServer.verify(...);
}
,
遵循并深入研究文档和测试之后。
我发现您可以指定一个“时间”来匹配期望值 这完美地解决了我的问题。
链接:https://www.mock-server.com/mock_server/creating_expectations.html
对于每个期望,我都使用了“Times.exactly(1)”。
这种工作方式是在匹配时将期望添加到列表中 它将被消耗,并从列表中删除,留下以下列表。
如果未找到调用预期,它将从模拟服务器返回 404。
正确的代码:
//The first call will land here,and then this expectation will be deleted,remaining the next one
mockServer
.when(
request()
.withPath(testUrlValidateTransactionOk).withMethod(HttpMethod.POST.name())
.withHeaders(
new Header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON.toString())
)
.withBody(
json("{\"id\":1}",MatchType.ONLY_MATCHING_FIELDS)),Times.exactly(1)
).respond(
response().withStatusCode(HttpStatus.OK.value())
.withHeaders(
new Header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON.toString())
)
.withBody("{\"passed\":true}"));
//After the first call this will be consumed and removed,leaving no expectations
mockServer
.when(
request()
.withPath(testUrlValidateTransactionOk).withMethod(HttpMethod.POST.name())
.withHeaders(
new Header(HttpHeaders.CONTENT_TYPE,MediaType.APPLICATION_JSON.toString())
)
.withBody("{\"passed\":false}"));