【问题标题】:PACT - Using provider statePACT - 使用提供者状态
【发布时间】:2017-09-12 18:26:53
【问题描述】:

我正在尝试使用 pact 来验证 Spring Boot 微服务。我已经从消费者生成了协议文件,并使用协议代理在提供者端对其进行了验证。

我有另一个用例,我需要在根据实际服务响应验证协议文件之前执行一些代码。我阅读了有关状态更改 URL 和状态更改的内容以实现它,但无法获得如何实现此目的的示例。有人可以帮忙吗?

我的具体情况是:我创建了一个合同来更新 id 为 1234 的客户(名字:测试姓氏:用户)。

如果此客户不存在,那么我需要通过读取 pact 文件中更新请求中的名字、姓氏、id 以及通过状态更改的附加信息(城市、州、电话号码)来将此数据插入 DB代码。

所以我的问题是,我可以通过状态更改从 pact 文件中读取请求数据,而不是在验证端配置名字、姓氏和 id 吗?

【问题讨论】:

  • 请提供您尝试过的示例。

标签: pact


【解决方案1】:

状态更改 URL 是您在提供程序上创建的一个钩子,它允许 Pact 告诉提供程序在测试开始时它应该处于什么状态。在每个测试运行之前,模拟消费者点击您的提供者上的状态更改 URL,并告诉它测试期望的状态名称。

你需要做两件事:

  1. 配置状态更改 URL
  2. 在提供程序上实现状态更改端点

配置状态改变 URL

您可以在提供商验证设置中配置状态更改 URL。例如,使用the maven plugin:

<serviceProvider>
  <name>provider1</name>
  <stateChangeUrl>http://localhost:8080/tasks/pactStateChange</stateChangeUrl>
...

或者使用Gradle provider plugin

hasPactWith('consumer1') {
 stateChangeUrl = url('http://localhost:8080/tasks/pactStateChange')
...

这两个都告诉模拟消费者使用localhost:8080/tasks/pactStateChange 在每次测试之前更改提供者的状态。

实现状态改变端点

上面链接的文档告诉我们,默认情况下,请求的格式是您的状态字符串和任何参数的 POST 请求:

{ "state" : "a provider state description", "params": { "a": "1", "b": "2" } }

要使用它,您需要在提供程序上实现以下未经测试的代码:

@RequestMapping(value = "tasks/pactStateChange", method = RequestMethod.POST)
ResponseEntity<?> stateChange(@RequestBody ProviderState state) {
   if (state.state == "no database") {
       // Set up state for the "no database" case here
   } else if state.state == "Some other state" {
       // Set up state here
   } else if  ...   // Other states go here
   ... 
   }

   return ResponseEntity.ok().build()
}

请原谅该示例中的任何 Spring Boot 错误 - 我不是 Spring Boot 人,但您可以看到一般原则。

使用状态更改 URL,pact 不会告诉提供者任何设置细节。它只是告诉提供者您在测试中使用的预先同意的状态字符串。这可能类似于"foo exists"。然后,在实现状态更改 URL 的处理程序时,您会检测到"foo exists",并在那里进行任何显式设置。

if (state.state == "foo exists") {
     // do whatever you need to set up so that foo exists
    repository.clear()
    repository.insert(new Foo("arguments that foo needs",12))
}

如果您想了解更多关于提供者状态的意图,请阅读wiki page on provider states

如何在您的特定情况下执行此操作

你问:

我可以通过状态更改从pact文件中读取请求数据,而不是在验证端配置名字、姓氏和id吗?

您可能对合同测试的意图感到困惑 - 每个测试都是状态和请求的组合。

所以不要用一个测试来说:

  • 我的测试是请求客户更新。如果客户存在,那么我期望 X 响应,如果不存在,那么我期望 Y 响应

你用两个测试说:

  • 当我向客户记录提交更新时(在客户存在时的状态),我期待 X 响应。

  • 当我提交对客户记录的更新(在客户不存在的状态下)时,我希望得到 Y 响应。

这些测试是您 Pact 合同中的两个独立项目。

我们的意图不是在合同中包含设置的详细信息。在消费者方面,您的状态只是一个字符串,上面写着“存在 id=1234 的客户”。

在 Provider 端,您的状态更改端点会检测到该 URL 并根据需要创建状态。这通常以硬编码的方式完成:

if (state == "Customer with id=1234 exists") {
  Database.Clear()
  Database.Insert(new Customer(1234, "John","Smith")) 
} else if (state == "No customers exist") { 
  Database.Clear()
}

您不希望通过解析状态字符串以参数化方式执行此操作,因为这样您就在测试消费者和提供者之间创建了一个新的复杂合同。

消费者测试不应该知道如何设置提供者状态,他们应该只知道测试需要什么状态(仅按名称)。同样,提供者不需要知道正在测试什么,它只需要知道如何将状态名称转换为实际状态。

【讨论】:

  • 感谢蒂莫西的回复。这真的很有帮助。作为此状态更改的一部分,是否可以修改协议文件中定义的请求。例如,我正在发送带有 5 个参数的更新请求作为我的协议文件的一部分,并且我想确保记录存在于状态更改的数据库中,希望在访问数据库之前向更新请求属性添加另外 2 个参数。这可能吗?
  • 我不确定我是否理解。我已将指向 wiki 相关部分的链接编辑到我的答案中,但如果这没有帮助,您能否编辑您的问题以包含您正在尝试做什么的明确细节? (您的测试示例会很棒)。
  • 这是我要测试的用例,我创建了一个合同来更新 id 为 1234 的客户(名字:测试姓氏:用户)。如果这个客户不存在,那么我需要通过从协议文件中的更新请求中读取名字、姓氏、id 以及通过状态更改代码的附加信息(城市、州、电话号码)来将此数据插入数据库。所以我的问题是,我可以通过状态更改从 pact 文件中读取请求数据,而不是在验证端配置名字、姓氏和 id 吗?
  • 非常感谢,您已经解释得很清楚了。我现在明白了。
  • 谢谢。它在帮助像我这样的人,即使是在发布一年后。
猜你喜欢
  • 2021-12-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-02
  • 2016-04-16
  • 1970-01-01
相关资源
最近更新 更多