将pact用于具有一系列请求和响应的服务

时间:2017-12-26 08:54:18

标签: pact

这是我的用例:

我的服务是:ServiceA。
它取决于以下服务:ServicesB和ServiceC。

ServiceA向ServiceB发送POST请求,其中包含一些身份验证详细信息(用户名+密码),ServiceB使用具有sessionId的json文档回复。

请求:

POST /authenticate
{
    "username": "_at_api",
    "password": "xxx"
}

响应:

{
    "sessionId": "axy235da7ad5a24abeb3e7fbb85d0ef45f"
}

以上sessionId用于从ServiceA到ServiceC的所有api调用。

ServiceA请求serviceC使用POST请求启动作业,serviceC返回作业ID(字母数字)。

请求:

POST /jobs/local/start
Header: Authentication: axy235da7ad5a24abeb3e7fbb85d0ef45f
{
    ...
}

响应:

{
    "status": "RUNNING",
    "jobId": "a209016e3fdf4425ea6e5846b8a46564abzt"
}

ServiceA使用上面返回的jobId继续轮询serviceC以完成作业:

请求:

GET /jobs/status/a209016e3fdf4425ea6e5846b8a46564abzt
Header: Authentication: axy235da7ad5a24abeb3e7fbb85d0ef45f

响应:

{
    "status": "RUNNING"
}

轮询继续,直到状态返回为COMPLETED或FAILED。

响应:

{
    "status": "COMPLETED"
}

如何使用Pact测试serviceA?

我的计划是仅使用单元测试和合同测试来实现超过90%的代码覆盖率。这是一个好主意,还是我需要使用虚拟服务器进行单独的测试?我的理解是Pact是虚拟服务器的超集(例如:mountebank)以及虚拟服务器可以做的所有事情,Pact可以做到。所以我不需要单独的组件测试。此外,看起来合同测试完全取代了端到端测试,因此我也不需要端到端测试。这是对的吗?

2 个答案:

答案 0 :(得分:6)

  

此外,看起来合同测试完全取代了端到端测试,因此我也不需要端到端测试。这是对的吗?

没有。合同测试不是功能测试(参见this excellent article, with the same title

什么是合同测试?

合同测试是关于测试两个组件是否能够通信。

考虑房屋和邮政工人之间的合同:邮政工人需要知道他们可以接近房子并发送邮件(有时他们可能无法做到这一点 - 也许邮箱已满)。

从邮政工作人员的角度来看,合同如下:

  • 查找邮箱(成功和失败案例)
  • 将帖子发送到邮箱(成功和失败案例)

请注意,邮政工作人员对邮箱的实施一无所知。也许有多种原因导致邮箱发送可能会失败 - 可能是门被堵塞,也许盒子已满 这个帖子太大了,不适合它。

在这个假设的案例中,我们的邮政工作人员在这些案件中没有做任何不同的事情 - 他们只是无法交付。因此,从合同的角度来看,失败的原因是无关紧要的。合同 - 工人可以尝试交付帖子,并且他们可以成功或不成功 - 可以在不列举所有可能的失败原因的情况下进行测试。

请参阅文章linked above以获取更详细的示例,但引用它的结尾:

  

合同应该是关于捕捉:

     
      
  • 消费者中的错误
  •   
  • 消费者对终点或有效负载的误解
  •   
  • 中断提供商对端点或有效负载的更改
  •   

Pact的一个非常好的功能是,您可以仅针对他们所依赖的通信测试多个合同。

请注意,消费者合同测试仅描述消费者需要制作或理解的沟通。合同不是(必然)完整的API描述。

好的,但为什么无法我使用合同测试进行端到端测试?

可能使用像Pact这样的工具来替换您的端到端测试。但是,虽然合同测试与端到端测试所需的功能有很多相似之处,但合同测试(特别是Pact)并不是为端到端测试而设计的。

如果您通过扩展现有的消费者测试(例如,将所有可能的失败原因添加到后期工作者的测试中)来进行端到端测试,那么它就是&#39 ; s不再清楚合同的含义。合同现在描述了沟通如何与行为一起发挥作用。

当您开始添加更多消费者(例如,包裹快递)时,这会导致问题 - 您是否复制了所有消费者中的所有失败案例,或者您是否只是将其保留在原始的消费者测试中?如果您复制测试,那么如果您更改提供程序的行为,则需要更改许多内容 - 并且您的测试将会很脆弱。如果你不复制测试,那么你的端到端测试会被困在一个消费者身上 - 如果你让这个消费者退役,那么所有的问题都会丢失。

通过纯合同测试,如果您为消费者已经理解的失败原因添加更多可能的原因,您(理想情况下)不必更改任何内容。

如果您尝试这样做,还有许多其他原因让您头疼(您的测试开始严重依赖于确切的数据,如果测试结束,则验证失败和can-i-deploy挂钩的含义会发生变化 - 结束测试),但关键的一点是,Pact不是为了替代端到端测试而设计的。您可以这样使用,但这不可取,可能会导致痛苦的维护。

如何使用Pact测试serviceA?

您可以使用Pact provider state作为每个请求的先决条件,单独描述每个请求。

此外,您可以在PACT - Using provider state上找到有用的问题。

答案 1 :(得分:0)

尽管来晚了,但对于偶然发现此问题的每个人来说:

pact规范(v3.0)每次互动仅支持一个请求(但每个消费者-提供者对都支持多个互动)。在提供者方面,每次交互都会导致一个单独的测试。因此,虽然从概念上讲,在提供者端运行序列或请求并不是一个好主意,但从技术上讲也行不通。

但是在消费者方面,您有另一种选择-我鼓励您不要滥用它进行端到端测试。就我而言,我有一个实现了模板/策略模式的类,该模板涉及多个请求,并且不允许在消费者端注入中间状态。它要求所有请求都具有有效的响应。

在这种情况下,pact-jvm-consumer-junit(= JUnit4)允许在单个测试方法(在我的情况下为Kotlin)中指定多个PactVerification注释:


@Test
@PactVerifications(
    value = [
        PactVerification("Service B", fragment = "authenticateOk"),
        PactVerification("Service C", fragment = "jobStartOk")
        PactVerification("Service C", fragment = "jobStatusOk")
    ]
)
fun `test successful job execution`() {
 // add your test here.
}

在提供者方面,以上所有片段都作为单独的测试执行,因此它们需要如上面答案中所指定的正确状态。

我在JUnit4中运行了这种方法。但是,还没有找到模仿它的快速方法。