使用java创建PACT,我无法设置正文中numberType的最小值

使用java创建PACT,我无法设置正文中numberType的最小值,java,pact,pact-jvm,pact-java,Java,Pact,Pact Jvm,Pact Java,我正在学习如何在Java项目中使用PACT,我想对预期输出定义一些值限制。例如,在一个请求/hello world中,我希望在id属性中收到一个始终大于零的数字 package com.thiagomata.pact.hello.consumer.consumer; import au.com.dius.pact.consumer.ConsumerPactBuilder; import au.com.dius.pact.consumer.PactVerificationResult; impo

我正在学习如何在Java项目中使用PACT,我想对预期输出定义一些值限制。例如,在一个请求
/hello world
中,我希望在id属性中收到一个始终大于零的数字

package com.thiagomata.pact.hello.consumer.consumer;

import au.com.dius.pact.consumer.ConsumerPactBuilder;
import au.com.dius.pact.consumer.PactVerificationResult;
import static io.pactfoundation.consumer.dsl.LambdaDsl.newJsonBody;
import au.com.dius.pact.consumer.dsl.PactDslJsonBody;
import au.com.dius.pact.model.MockProviderConfig;
import au.com.dius.pact.model.RequestResponsePact;
import com.thiagomata.pact.hello.consumer.models.Greeting;
import io.pactfoundation.consumer.dsl.LambdaDslJsonBody;
import org.junit.Assert;
import org.junit.Test;
import scala.tools.jline_embedded.internal.Log;

import static au.com.dius.pact.consumer.ConsumerPactRunnerKt.runConsumerTest;
import static org.junit.Assert.assertEquals;

public class NameApplicationPactTest {

    @Test
    public void testNamePact() throws Throwable {

        Log.debug("inside the test");
        /**
         * Creating the mock server
         *
         * Define the expected input
         *  Using relative address
         *  The provider address will be automatically created
         *  The provider port will be automatically created
         * Define the expected output
         *  Keep the id as a undefined integer
         *  Set the content to the test
         */
        RequestResponsePact pact = ConsumerPactBuilder
            .consumer("hello_world_consumer")
            .hasPactWith("hello_world_provider")
            .uponReceiving("a request of hello world")
            .path("/hello-world")
            .matchQuery("name","johny")
            .method("GET")
            .willRespondWith()
            .body(
                newJsonBody( (LambdaDslJsonBody o) -> o.
                    numberType("id"). // <====================================
                    stringType("content", "Hello johny")
                ).build()
            )
            .toPact();

        /**
         * Let the Pact define the mock server address and port
         */
        MockProviderConfig config = MockProviderConfig.createDefault();

        /**
         * Create the mock server into the defined config and with the
         * pact result prepared
         */
        PactVerificationResult result = runConsumerTest(
            pact,
            config,
            mockServer -> {

                Log.debug("inside mock server");
                /**
                 * Pass the mock server configuration to the consumer classes
                 */
                DummyConsumer consumer = new DummyConsumer(
                    mockServer.getUrl(),
                    mockServer.getPort(),
                    "johny"
                );

                /**
                 * Now, when the code internally fires to the
                 * mockServer we should get the expected answer
                 */
                Greeting greeting = consumer.getGreeting();

                Log.debug(greeting);

                Assert.assertNotNull(
                    "Greeting id should not be null",
                    greeting.getId()
                );

                /**
                 * Currently I am not able to define a rule into the
                 * DSL Matching methods to assure that the value should
                 * be bigger than 0
                 */
                Assert.assertTrue( greeting.getId() > 0 ); // <=================================================

                assertEquals(
                    "Validate expected default greeting content",
                    "Hello johny",
                    greeting.getContent()
                );

                Log.debug("status code = " + consumer.getStatusCode() );

                Assert.assertTrue(
                    "test consumer status code",
                    consumer.getStatusCode().equals(
                        200
                    )
                );
            }
        );

        /**
         * If some Assert inside of the anonymous functions fails
         * it will not automatically throw a failure.
         *
         * We need to capture the error from the result
         */
        if (result instanceof PactVerificationResult.Error) {
            throw ((PactVerificationResult.Error) result).getError();
        }

        assertEquals(PactVerificationResult.Ok.INSTANCE, result);
    }
}
该方法使用以下可能的方法调用
对象。numberTypes
将其放入
lambdasljsonbody

/**
 * Attribute that can be any number
 * @param name attribute name
 */
public PactDslJsonBody numberType(String name) {
    generators.addGenerator(
        Category.BODY, 
        matcherKey(name), 
        new RandomIntGenerator(0, Integer.MAX_VALUE) // <========================
    );
    return numberType(name, 100);
}

/**
 * Attributes that can be any number
 * @param names attribute names
 */
public PactDslJsonBody numberType(String... names) {
  for (String name: names) {
    numberType(name);
  }
  return this;
}

/**
 * Attribute that can be any number
 * @param name attribute name
 * @param number example number to use for generated bodies
 */
public PactDslJsonBody numberType(String name, Number number) {
    body.put(name, number);
    matchers.addRule(matcherKey(name), new NumberTypeMatcher(NumberTypeMatcher.NumberType.NUMBER));
    return this;
}
/**
*属性,该属性可以是任意数字
*@param name属性名
*/
公共PactDslJsonBody numberType(字符串名称){
发电机(
类别.机构,
matcherKey(姓名),
新的RandomIntGenerator(0,Integer.MAX_VALUE)/它是可行的(替换默认值生成器),但它需要应用一个小的解决方法。您可以将自定义生成器添加到方法返回的生成器列表中,类似于:

DslPart.getGenerators()
            .addGenerator(Category.BODY, ".id", new RandomIntGenerator(0, 100));
它将覆盖调用
.id
方法时创建的
.id
字段的生成器。查看此示例性消费者合同测试:

import au.com.dius.pact.consumer.Pact;
import au.com.dius.pact.consumer.PactProviderRuleMk2;
import au.com.dius.pact.consumer.PactVerification;
import au.com.dius.pact.consumer.dsl.DslPart;
import au.com.dius.pact.consumer.dsl.PactDslJsonBody;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.model.RequestResponsePact;
import au.com.dius.pact.model.generators.Category;
import au.com.dius.pact.model.generators.RandomIntGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Rule;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class PactIntGeneratorTest {

    @Rule
    public PactProviderRuleMk2 mockProvider = new PactProviderRuleMk2("providerA", "localhost", 8080, this);

    @Pact(consumer = "consumerA", provider = "providerA")
    public RequestResponsePact requestA(PactDslWithProvider builder) throws Exception {
        final DslPart body = new PactDslJsonBody()
                .numberType("id")
                .stringType("content", "Hello johny");

        body.getGenerators()
                .addGenerator(Category.BODY, ".id", new RandomIntGenerator(0, 100));

        return builder
                .uponReceiving("(GET) /foo")
                    .path("/foo")
                    .method("GET")
                .willRespondWith()
                    .status(200)
                    .body(body)
                .toPact();
    }

    @Test
    @PactVerification(fragment = "requestA")
    public void testRequestA() throws IOException, InterruptedException {
        //given:
        final ObjectMapper objectMapper = new ObjectMapper();

        //when:
        final InputStream json = new URL("http://localhost:8080/foo").openConnection().getInputStream();
        final Map response = objectMapper.readValue(json, HashMap.class);

        //then:
        assertThat(((Integer) response.get("id")) > 0, is(true));
        //and:
        assertThat(response.get("content"), is(equalTo("Hello johny")));
    }
}
这与您的情况不完全相同,但它显示了如何覆盖
$.id
字段的生成器。运行此测试后,将生成以下Pact文件:

{
    "provider": {
        "name": "providerA"
    },
    "consumer": {
        "name": "consumerA"
    },
    "interactions": [
        {
            "description": "(GET) /foo",
            "request": {
                "method": "GET",
                "path": "/foo"
            },
            "response": {
                "status": 200,
                "headers": {
                    "Content-Type": "application/json; charset=UTF-8"
                },
                "body": {
                    "id": 100,
                    "content": "Hello johny"
                },
                "matchingRules": {
                    "body": {
                        "$.id": {
                            "matchers": [
                                {
                                    "match": "number"
                                }
                            ],
                            "combine": "AND"
                        },
                        "$.content": {
                            "matchers": [
                                {
                                    "match": "type"
                                }
                            ],
                            "combine": "AND"
                        }
                    }
                },
                "generators": {
                    "body": {
                        "$.id": {
                            "type": "RandomInt",
                            "min": 0,
                            "max": 100
                        }
                    }
                }
            }
        }
    ],
    "metadata": {
        "pact-specification": {
            "version": "3.0.0"
        },
        "pact-jvm": {
            "version": "3.5.10"
        }
    }
}
如您所见,
RandomIntGenerator
与属性
min:0
max:100
一起使用

最后值得一提的是:契约测试和功能测试之间的区别
请记住,生成器仅用于生成在提供程序的契约验证测试运行时传递给提供程序的值。我创建的自定义生成器不会修改契约-它不会说只有0到100之间的值才是正确的值。它只会在提供程序执行时生成此范围内的值es合同验证。因此,您的合同仍然对
id
有效,如1001或12700等。这很好,因为消费者不应强制执行此类业务规则。否则,您可能会很快遇到这样的情况,
consumerA
id
在0和100之间时是正确的,而
消费者B
id
只有在99和999之间才是正确的。我知道创建非常具体的合同很有诱惑力,但这是一种直接的方法,过度规范限制了供应商的开发。希望它能有所帮助。

回答很好。使用您发送给我的信息,我能够做到这一点。我不知道为什么,但使用匿名版本的t他模拟服务器提供商,这种方法不起作用。它甚至会更改其他交互的Json!这需要我几个小时才能弄清楚。因此,如果有人使用Java PACT的匿名函数版本,请注意它有一些严重的限制。我将运行版本的代码发布到了我发布的最终版本int中我创建了这个类,它提供了我所需要的方法。我无法使它与工作,但只能与工作
import au.com.dius.pact.consumer.Pact;
import au.com.dius.pact.consumer.PactProviderRuleMk2;
import au.com.dius.pact.consumer.PactVerification;
import au.com.dius.pact.consumer.dsl.DslPart;
import au.com.dius.pact.consumer.dsl.PactDslJsonBody;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.model.RequestResponsePact;
import au.com.dius.pact.model.generators.Category;
import au.com.dius.pact.model.generators.RandomIntGenerator;
import org.codehaus.jackson.map.ObjectMapper;
import org.junit.Rule;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;

public class PactIntGeneratorTest {

    @Rule
    public PactProviderRuleMk2 mockProvider = new PactProviderRuleMk2("providerA", "localhost", 8080, this);

    @Pact(consumer = "consumerA", provider = "providerA")
    public RequestResponsePact requestA(PactDslWithProvider builder) throws Exception {
        final DslPart body = new PactDslJsonBody()
                .numberType("id")
                .stringType("content", "Hello johny");

        body.getGenerators()
                .addGenerator(Category.BODY, ".id", new RandomIntGenerator(0, 100));

        return builder
                .uponReceiving("(GET) /foo")
                    .path("/foo")
                    .method("GET")
                .willRespondWith()
                    .status(200)
                    .body(body)
                .toPact();
    }

    @Test
    @PactVerification(fragment = "requestA")
    public void testRequestA() throws IOException, InterruptedException {
        //given:
        final ObjectMapper objectMapper = new ObjectMapper();

        //when:
        final InputStream json = new URL("http://localhost:8080/foo").openConnection().getInputStream();
        final Map response = objectMapper.readValue(json, HashMap.class);

        //then:
        assertThat(((Integer) response.get("id")) > 0, is(true));
        //and:
        assertThat(response.get("content"), is(equalTo("Hello johny")));
    }
}
{
    "provider": {
        "name": "providerA"
    },
    "consumer": {
        "name": "consumerA"
    },
    "interactions": [
        {
            "description": "(GET) /foo",
            "request": {
                "method": "GET",
                "path": "/foo"
            },
            "response": {
                "status": 200,
                "headers": {
                    "Content-Type": "application/json; charset=UTF-8"
                },
                "body": {
                    "id": 100,
                    "content": "Hello johny"
                },
                "matchingRules": {
                    "body": {
                        "$.id": {
                            "matchers": [
                                {
                                    "match": "number"
                                }
                            ],
                            "combine": "AND"
                        },
                        "$.content": {
                            "matchers": [
                                {
                                    "match": "type"
                                }
                            ],
                            "combine": "AND"
                        }
                    }
                },
                "generators": {
                    "body": {
                        "$.id": {
                            "type": "RandomInt",
                            "min": 0,
                            "max": 100
                        }
                    }
                }
            }
        }
    ],
    "metadata": {
        "pact-specification": {
            "version": "3.0.0"
        },
        "pact-jvm": {
            "version": "3.5.10"
        }
    }
}