使用java创建PACT我无法在body中设置numberType的最小值

问题描述 投票:3回答:1

我正在学习如何将PACT用于我的Java项目,我想在预期的输出上定义一些值限制。例如,在一个请求/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代码。因此,遵循numberType方法的轨道,LambdaDsl

/* ... */

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

/* ... */

该方法使用这种可能的方法将object.numberTypes调用到LambdaDslJsonBody

/**
 * 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?

java pact pact-jvm pact-java
1个回答
3
投票

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

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

它会覆盖在调用$.id方法时创建的.numberType("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"
        }
    }
}

正如你所看到的,RandomIntGeneratormin:0max:100属性一起使用。

最后值得一提的是:合同测试和功能测试之间的区别

请记住,生成器仅用于生成在提供程序的合同验证测试运行时传递给提供程序的值。我创建的自定义生成器不会修改合同 - 它不会说只有0到100之间的值是正确的。当提供者执行合同验证时,它只会在此范围内生成一个值。所以你的合同仍然适用于id,如1001或12700等。这很好,因为contract tests are not functional tests。消费者不应强迫这些业务规则。否则你可能会很快遇到consumerA在0到100之间说id是正确的情况,而consumerBid只有在99到999之间才是正确的。我知道创建非常具体的合同是很诱人的,但它是直的过度规范的方式,使提供者从开发中获得。希望能帮助到你。

© www.soinside.com 2019 - 2024. All rights reserved.