使用Java创建PACT我无法在主体中设置numberType的最小值

我正在学习如何在我的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);
    }
}

有人可以说PACT它不能应用这样的限制.
但是,从生成的PACT来看,似乎可以为生成器创建最小值和最大值应成为PACT的一种可能:

    {
      "provider": {
        "name": "hello_world_provider"
      },
      "consumer": {
        "name": "hello_world_consumer"
      },
      "interactions": [
        {
          "description": "Test User Service",
          "request": {
            "method": "GET",
            "path": "/hello-world"
          },
          "response": {
            "status": 200,
            "headers": {
              "content-type": "application/json",
              "Content-Type": "application/json; charset\u003dUTF-8"
            },
            "body": {
              "id": 100,
              "content": "string"
            },
            "matchingRules": {
              "body": {
                "$.id": {
                  "matchers": [
                    {
                      "match": "integer"
                    }
                  ],
                  "combine": "AND"
                },
                "$.content": {
                  "matchers": [
                    {
                      "match": "type"
                    }
                  ],
                  "combine": "AND"
                }
              }
            },
            "generators": {
              "body": {
                "$.id": {
                  "type": "RandomInt",
                  "min": 0, /* <======================================== */
                  "max": 2147483647
                },
                "$.content": {
                  "type": "RandomString",
                  "size": 20
                }
              }
            }
          },
          "providerStates": [
            {
              "name": "default"
            }
          ]
        }
      ],
      "metadata": {
        "pact-specification": {
          "version": "3.0.0"
        },
        "pact-jvm": {
          "version": "3.5.10"
        }
      }
    }

我尝试找到一些方法,查看PACT代码.因此,遵循LambdaDsl的numberType方法:

/* ... */

public LambdaDslObject numberType(final String... names) {
    object.numberType(names);
    return this;
}

/* ... */

该方法使用以下可能的方法将其放入LambdaDslJsonBody中的object.numberTypes:

/**
 * 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;
}

只有一台发电机的地方,总是以零开始.

因此,有一种可能的方法可以将这种随机数生成器创建为PACT,以确保生成的随机数的值将大于零或小于100?

解决方法:

这是可行的(替换默认值生成器),但是需要应用小的解决方法.您可以将自定义生成器添加到DslPart.getGenerators()方法返回的生成器列表中,例如:

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

当调用.numberType(“ 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之间的值才是正确的.仅当提供商执行合同验证时,它才会生成此范围内的值.因此,您的合同对于1001或12700等ID仍然有效.这很好,因为contract tests are not functional tests.消费者不应强制执行此类业务规则.否则,您可能会很快遇到如下情况:消费者A说id在0到100之间是正确的,而消费者B说id只有在99到999之间才是正确的.我知道创建非常具体的合同很诱人,但这是直截了当的规格过高的方法使提供程序无法进行开发.希望能帮助到你.

上一篇:【PTA题目解答】7-1 输出所有大于平均值的数


下一篇:invalid comparison: java.util.Arrays$ArrayList and java.lang.String