pact-jvm:如何解决 au.com.dius.pact.consumer.PactMismatchesException
pact-jvm : how to solve au.com.dius.pact.consumer.PactMismatchesException
我正在尝试使用 pact-JVM 在我们的 2 个服务之间生成协议。但是当我尝试 运行 Java class 时,我得到了这个异常。
1) 我怀疑 Pact 本体有问题,对吗? PactDslWithProvider 的 JSON 主体中有一个额外的 'message' 参数,但在 运行Test1() 方法中,我只等同于列表,当我检查结果时,它们是和我一样。
2) 在 运行Test1() 方法中提供实际提供者 URL 是否正确? (提供商已经到位)
au.com.dius.pact.consumer.PactMismatchesException: The following requests were not received:
method: GET
path: /devices/v1
query: [externalId:[0942dc67-35de-44f7-a061-743f59436a98]]
headers: [:]
matchers: MatchingRules(rules=[:])
generators: Generators(categories={})
body: OptionalBody(state=MISSING, value=null)
下面是我的Javaclass
public class PactForDevice {
Map<String, String> headers = MapUtils.putAll(new HashMap<String, String>(), new String[]{"Content-Type", "application/json;charset=UTF-8"});
@Rule
public PactProviderRuleMk2 provider = new PactProviderRuleMk2("device-service-m", this);
@Pact(consumer = "device-r", provider = "device-service-m")
public RequestResponsePact createFragment(PactDslWithProvider builder) {
return builder
.given("Device M details")
.uponReceiving("retrieving Device details")
.path("/devices/v1")
.method("GET")
.query("externalId=0942dc67-35de-44f7-a061-743f59436a98")
.willRespondWith()
.headers(headers)
.status(200)
.body("{" +
"\"data\": [,\n " +
"{ \n" +
" \"dateRegistered\": \"2017-07-13T11:10:51.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"a02b14ee72192ab3\",\n" +
" \"description\": \"Samsung SM-G930F\",\n" +
" \"title\": \"a02b14ee72192ab3\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
"},\n" +
"{\n" +
" \"dateRegistered\": \"2017-07-13T10:45:51.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"a41c3af56ec35874\",\n" +
" \"description\": \"Samsung SM-T819\",\n" +
" \"title\": \"a41c3af56ec35874\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
" },\n" +
" {\n" +
" \"dateRegistered\": \"2017-07-13T10:45:31.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"bd2b027bbd0a2f17\",\n" +
" \"description\": \"Samsung SM-A320Y\",\n" +
" \"title\": \"bd2b027bbd0a2f17\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
" }\n" +
"],\n" +
" \"message\": \"3 devices found for the user 0942dc67-35de-44f7-a061-743f59436a98\"\n" +
"}")
.toPact();
}
@PactVerification("device-service-m")
@Test
@JsonIgnoreProperties(ignoreUnknown = true)
public void runTest1() throws IOException {
final GetDevicesResponse deviceResponse = new GetDevicesResponse();
final List<Device> deviceList = new ArrayList<>();
Device dev = new Device();
dev.withDateRegistered("2017-07-13T11:10:51.000+12:00");
dev.withAlias("");
dev.withId("a02b14ee72192ab3");
dev.withDescription("Samsung SM-G930F");
dev.withTitle("a02b14ee72192ab3");
dev.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev);
Device dev1 = new Device();
dev1.withDateRegistered("2017-07-13T10:45:51.000+12:00");
dev1.withAlias("");
dev1.withId("a41c3af56ec35874");
dev1.withDescription("Samsung SM-T819");
dev1.withTitle("a41c3af56ec35874");
dev1.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev1);
Device dev2 = new Device();
dev2.withDateRegistered("2017-07-13T10:45:31.000+12:00");
dev2.withAlias("");
dev2.withId("bd2b027bbd0a2f17");
dev2.withDescription("Samsung SM-A320Y");
dev2.withTitle("bd2b027bbd0a2f17");
dev2.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev2);
deviceResponse.setDevices(deviceList);
final RestTemplate restTemplate = new RestTemplate();
GetDevicesResponse devices = restTemplate.getForObject("http://localhost:8091/devices/v1?externalId=0942dc67-35de-44f7-a061-743f59436a98", GetDevicesResponse.class);
assertThat(devices, sameBeanAs(deviceResponse));
}
}
编辑:
我刚刚发现,如果我注释掉@Rule 部分,测试就会通过 - 但不会生成协议文件。我应该明确指定一个 "pact" 文件夹吗?
您的测试存在一些问题。
问题 #1
您没有为 Pact 提供程序规则指定端口,因此它在随机端口上启动模拟服务器。您的测试在端口 8091 上访问您的提供程序,因此 Pact 没有通过测试并报告它没有收到预期的请求,而实际上它没有(请求转到其他监听端口 8091 的东西)。
您可以通过向规则提供端口 8091(您需要关闭 8091 上的任何 运行)或让您的客户端使用模拟服务器的端口(来自调用 getMockServer().getPort()
)。
问题#2
您的测试直接使用 Spring Rest 模板,这意味着除了 Spring HTTP 客户端和 bean 反序列化之外,它并没有真正测试任何东西。您应该使用您拥有的任何客户端代码(即使用其余模板的 class)并在测试中调用它。
我有一个类似的问题,测试后没有生成契约 运行。我从来没有让他们使用注释方法工作,而是通过扩展 ConsumerPactTestMk2 来解决它。 Pact 将为您设置模拟服务器并模拟响应。
public class PactForDevice extends ConsumerPactTestMk2 {
Map<String, String> headers = MapUtils.putAll(new HashMap<String, String>(), new String[]{"Content-Type", "application/json;charset=UTF-8"});
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("Device M details")
.uponReceiving("retrieving Device details")
.path("/devices/v1")
.method("GET")
.query("externalId=0942dc67-35de-44f7-a061-743f59436a98")
.willRespondWith()
.headers(headers)
.status(200)
.body("{" +
"\"data\": [,\n " +
"{ \n" +
" \"dateRegistered\": \"2017-07-13T11:10:51.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"a02b14ee72192ab3\",\n" +
" \"description\": \"Samsung SM-G930F\",\n" +
" \"title\": \"a02b14ee72192ab3\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
"},\n" +
"{\n" +
" \"dateRegistered\": \"2017-07-13T10:45:51.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"a41c3af56ec35874\",\n" +
" \"description\": \"Samsung SM-T819\",\n" +
" \"title\": \"a41c3af56ec35874\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
" },\n" +
" {\n" +
" \"dateRegistered\": \"2017-07-13T10:45:31.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"bd2b027bbd0a2f17\",\n" +
" \"description\": \"Samsung SM-A320Y\",\n" +
" \"title\": \"bd2b027bbd0a2f17\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
" }\n" +
"],\n" +
" \"message\": \"3 devices found for the user 0942dc67-35de-44f7-a061-743f59436a98\"\n" +
"}")
.toPact();
}
@Override
protected String providerName() {
return "device-service-m";
}
@Override
protected String consumerName() {
return "device-r";
}
@Override
protected void runTest(MockServer mockServer) throws IOException {
final GetDevicesResponse deviceResponse = new GetDevicesResponse();
final List<Device> deviceList = new ArrayList<>();
Device dev = new Device();
dev.withDateRegistered("2017-07-13T11:10:51.000+12:00");
dev.withAlias("");
dev.withId("a02b14ee72192ab3");
dev.withDescription("Samsung SM-G930F");
dev.withTitle("a02b14ee72192ab3");
dev.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev);
Device dev1 = new Device();
dev1.withDateRegistered("2017-07-13T10:45:51.000+12:00");
dev1.withAlias("");
dev1.withId("a41c3af56ec35874");
dev1.withDescription("Samsung SM-T819");
dev1.withTitle("a41c3af56ec35874");
dev1.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev1);
Device dev2 = new Device();
dev2.withDateRegistered("2017-07-13T10:45:31.000+12:00");
dev2.withAlias("");
dev2.withId("bd2b027bbd0a2f17");
dev2.withDescription("Samsung SM-A320Y");
dev2.withTitle("bd2b027bbd0a2f17");
dev2.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev2);
deviceResponse.setDevices(deviceList);
String url = mockServer.getUrl();
String path = "devices/v1";
String query = "externalId=0942dc67-35de-44f7-a061-743f59436a98";
URIBuilder uriBuilder = null;
try {
uriBuilder = new URIBuilder(url)
.setPath(path)
.setQuery(query);
} catch (URISyntaxException e) {
e.printStackTrace();
}
GetDevicesResponse devices = new ObjectMapper().readValue(Request.Get(uriBuilder.toString())
.addHeader("content-type", "application/json")
.execute().returnContent().asString(), GetDevicesResponse.class);
assertThat(devices, sameBeanAs(deviceResponse));
}
}
通过这种方法,我必须将 google guava 19 添加到我的 pom.xml 文件中。不过效果不错
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
我正在尝试使用 pact-JVM 在我们的 2 个服务之间生成协议。但是当我尝试 运行 Java class 时,我得到了这个异常。
1) 我怀疑 Pact 本体有问题,对吗? PactDslWithProvider 的 JSON 主体中有一个额外的 'message' 参数,但在 运行Test1() 方法中,我只等同于列表,当我检查结果时,它们是和我一样。 2) 在 运行Test1() 方法中提供实际提供者 URL 是否正确? (提供商已经到位)
au.com.dius.pact.consumer.PactMismatchesException: The following requests were not received:
method: GET
path: /devices/v1
query: [externalId:[0942dc67-35de-44f7-a061-743f59436a98]]
headers: [:]
matchers: MatchingRules(rules=[:])
generators: Generators(categories={})
body: OptionalBody(state=MISSING, value=null)
下面是我的Javaclass
public class PactForDevice {
Map<String, String> headers = MapUtils.putAll(new HashMap<String, String>(), new String[]{"Content-Type", "application/json;charset=UTF-8"});
@Rule
public PactProviderRuleMk2 provider = new PactProviderRuleMk2("device-service-m", this);
@Pact(consumer = "device-r", provider = "device-service-m")
public RequestResponsePact createFragment(PactDslWithProvider builder) {
return builder
.given("Device M details")
.uponReceiving("retrieving Device details")
.path("/devices/v1")
.method("GET")
.query("externalId=0942dc67-35de-44f7-a061-743f59436a98")
.willRespondWith()
.headers(headers)
.status(200)
.body("{" +
"\"data\": [,\n " +
"{ \n" +
" \"dateRegistered\": \"2017-07-13T11:10:51.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"a02b14ee72192ab3\",\n" +
" \"description\": \"Samsung SM-G930F\",\n" +
" \"title\": \"a02b14ee72192ab3\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
"},\n" +
"{\n" +
" \"dateRegistered\": \"2017-07-13T10:45:51.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"a41c3af56ec35874\",\n" +
" \"description\": \"Samsung SM-T819\",\n" +
" \"title\": \"a41c3af56ec35874\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
" },\n" +
" {\n" +
" \"dateRegistered\": \"2017-07-13T10:45:31.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"bd2b027bbd0a2f17\",\n" +
" \"description\": \"Samsung SM-A320Y\",\n" +
" \"title\": \"bd2b027bbd0a2f17\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
" }\n" +
"],\n" +
" \"message\": \"3 devices found for the user 0942dc67-35de-44f7-a061-743f59436a98\"\n" +
"}")
.toPact();
}
@PactVerification("device-service-m")
@Test
@JsonIgnoreProperties(ignoreUnknown = true)
public void runTest1() throws IOException {
final GetDevicesResponse deviceResponse = new GetDevicesResponse();
final List<Device> deviceList = new ArrayList<>();
Device dev = new Device();
dev.withDateRegistered("2017-07-13T11:10:51.000+12:00");
dev.withAlias("");
dev.withId("a02b14ee72192ab3");
dev.withDescription("Samsung SM-G930F");
dev.withTitle("a02b14ee72192ab3");
dev.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev);
Device dev1 = new Device();
dev1.withDateRegistered("2017-07-13T10:45:51.000+12:00");
dev1.withAlias("");
dev1.withId("a41c3af56ec35874");
dev1.withDescription("Samsung SM-T819");
dev1.withTitle("a41c3af56ec35874");
dev1.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev1);
Device dev2 = new Device();
dev2.withDateRegistered("2017-07-13T10:45:31.000+12:00");
dev2.withAlias("");
dev2.withId("bd2b027bbd0a2f17");
dev2.withDescription("Samsung SM-A320Y");
dev2.withTitle("bd2b027bbd0a2f17");
dev2.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev2);
deviceResponse.setDevices(deviceList);
final RestTemplate restTemplate = new RestTemplate();
GetDevicesResponse devices = restTemplate.getForObject("http://localhost:8091/devices/v1?externalId=0942dc67-35de-44f7-a061-743f59436a98", GetDevicesResponse.class);
assertThat(devices, sameBeanAs(deviceResponse));
}
}
编辑:
我刚刚发现,如果我注释掉@Rule 部分,测试就会通过 - 但不会生成协议文件。我应该明确指定一个 "pact" 文件夹吗?
您的测试存在一些问题。
问题 #1
您没有为 Pact 提供程序规则指定端口,因此它在随机端口上启动模拟服务器。您的测试在端口 8091 上访问您的提供程序,因此 Pact 没有通过测试并报告它没有收到预期的请求,而实际上它没有(请求转到其他监听端口 8091 的东西)。
您可以通过向规则提供端口 8091(您需要关闭 8091 上的任何 运行)或让您的客户端使用模拟服务器的端口(来自调用 getMockServer().getPort()
)。
问题#2
您的测试直接使用 Spring Rest 模板,这意味着除了 Spring HTTP 客户端和 bean 反序列化之外,它并没有真正测试任何东西。您应该使用您拥有的任何客户端代码(即使用其余模板的 class)并在测试中调用它。
我有一个类似的问题,测试后没有生成契约 运行。我从来没有让他们使用注释方法工作,而是通过扩展 ConsumerPactTestMk2 来解决它。 Pact 将为您设置模拟服务器并模拟响应。
public class PactForDevice extends ConsumerPactTestMk2 {
Map<String, String> headers = MapUtils.putAll(new HashMap<String, String>(), new String[]{"Content-Type", "application/json;charset=UTF-8"});
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("Device M details")
.uponReceiving("retrieving Device details")
.path("/devices/v1")
.method("GET")
.query("externalId=0942dc67-35de-44f7-a061-743f59436a98")
.willRespondWith()
.headers(headers)
.status(200)
.body("{" +
"\"data\": [,\n " +
"{ \n" +
" \"dateRegistered\": \"2017-07-13T11:10:51.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"a02b14ee72192ab3\",\n" +
" \"description\": \"Samsung SM-G930F\",\n" +
" \"title\": \"a02b14ee72192ab3\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
"},\n" +
"{\n" +
" \"dateRegistered\": \"2017-07-13T10:45:51.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"a41c3af56ec35874\",\n" +
" \"description\": \"Samsung SM-T819\",\n" +
" \"title\": \"a41c3af56ec35874\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
" },\n" +
" {\n" +
" \"dateRegistered\": \"2017-07-13T10:45:31.000+12:00\",\n" +
" \"alias\": \"\",\n" +
" \"id\": \"bd2b027bbd0a2f17\",\n" +
" \"description\": \"Samsung SM-A320Y\",\n" +
" \"title\": \"bd2b027bbd0a2f17\",\n" +
" \"externalId\": \"0942dc67-35de-44f7-a061-743f59436a98\"\n" +
" }\n" +
"],\n" +
" \"message\": \"3 devices found for the user 0942dc67-35de-44f7-a061-743f59436a98\"\n" +
"}")
.toPact();
}
@Override
protected String providerName() {
return "device-service-m";
}
@Override
protected String consumerName() {
return "device-r";
}
@Override
protected void runTest(MockServer mockServer) throws IOException {
final GetDevicesResponse deviceResponse = new GetDevicesResponse();
final List<Device> deviceList = new ArrayList<>();
Device dev = new Device();
dev.withDateRegistered("2017-07-13T11:10:51.000+12:00");
dev.withAlias("");
dev.withId("a02b14ee72192ab3");
dev.withDescription("Samsung SM-G930F");
dev.withTitle("a02b14ee72192ab3");
dev.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev);
Device dev1 = new Device();
dev1.withDateRegistered("2017-07-13T10:45:51.000+12:00");
dev1.withAlias("");
dev1.withId("a41c3af56ec35874");
dev1.withDescription("Samsung SM-T819");
dev1.withTitle("a41c3af56ec35874");
dev1.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev1);
Device dev2 = new Device();
dev2.withDateRegistered("2017-07-13T10:45:31.000+12:00");
dev2.withAlias("");
dev2.withId("bd2b027bbd0a2f17");
dev2.withDescription("Samsung SM-A320Y");
dev2.withTitle("bd2b027bbd0a2f17");
dev2.withExternalId("0942dc67-35de-44f7-a061-743f59436a98");
deviceList.add(dev2);
deviceResponse.setDevices(deviceList);
String url = mockServer.getUrl();
String path = "devices/v1";
String query = "externalId=0942dc67-35de-44f7-a061-743f59436a98";
URIBuilder uriBuilder = null;
try {
uriBuilder = new URIBuilder(url)
.setPath(path)
.setQuery(query);
} catch (URISyntaxException e) {
e.printStackTrace();
}
GetDevicesResponse devices = new ObjectMapper().readValue(Request.Get(uriBuilder.toString())
.addHeader("content-type", "application/json")
.execute().returnContent().asString(), GetDevicesResponse.class);
assertThat(devices, sameBeanAs(deviceResponse));
}
}
通过这种方法,我必须将 google guava 19 添加到我的 pom.xml 文件中。不过效果不错
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>