如何为 MockServer 的相同请求设置不同的响应?
How to set up different responses for the same request to MockServer?
我在为具有完全相同请求的多个响应设置 MockServerClient 时遇到问题。
我读到,在“Times”的期望下,这可能会完成,但我无法使其适用于我的场景。
如果您使用此 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(...);
}
在关注并深入研究文档和测试之后。
我发现您可以指定一个“次”来匹配预期
完美解决了我的问题。
Link: https://www.mock-server.com/mock_server/creating_expectations.html
对于每个期望,我都使用了“Times.exactly(1)”。
它的工作方式是在匹配时将期望添加到列表中
它将被消耗,并从列表中删除,留下以下列表。
如果没有找到调用预期,它将 return 来自模拟服务器的 404。
Link 文档中的示例:
https://www.mock-server.com/mock_server/creating_expectations.html#button_match_request_by_path_exactly_twice
正确代码:
//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(
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\":false}"));
我在为具有完全相同请求的多个响应设置 MockServerClient 时遇到问题。
我读到,在“Times”的期望下,这可能会完成,但我无法使其适用于我的场景。
如果您使用此 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(...);
}
在关注并深入研究文档和测试之后。
我发现您可以指定一个“次”来匹配预期 完美解决了我的问题。
Link: https://www.mock-server.com/mock_server/creating_expectations.html
对于每个期望,我都使用了“Times.exactly(1)”。
它的工作方式是在匹配时将期望添加到列表中 它将被消耗,并从列表中删除,留下以下列表。
如果没有找到调用预期,它将 return 来自模拟服务器的 404。
Link 文档中的示例: https://www.mock-server.com/mock_server/creating_expectations.html#button_match_request_by_path_exactly_twice
正确代码:
//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(
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\":false}"));