如何为 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}"));