如何为对 MockServer 的相同请求设置不同的响应?

问题描述

我在为具有完全相同请求的多个响应设置 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

文档中示例的链接: 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("{\"passed\":false}"));

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...