Spring Cloud Contracts 能否处理具有不同 return http 状态代码的重复请求?
Can Spring Cloud Contracts handle repeated requests with different return http status codes?
我正在开发一个小型 REST 服务来处理一些数据并将其持久化(目前是 Oracle,致力于添加缓存)。
我们开始使用 Spring 云合同框架处理消费者驱动的合同。我们无法为我们打算如何实施该服务定义一份合同。
我们有一个 PUT 端点,它在正文中接受一个输入字段,这是我们持久化的主键。在我们的服务中,我们在数据库中查找该字段,如果存在,我们希望 return 数据库中的现有数据带有 200 状态代码,表示没有添加新记录。如果那个值还没有在数据库中,那么我们有一些逻辑来为它生成数据并将数据插入数据库中。然后,我们希望 return 201 表示数据已创建。
我们有一个 201 案例的工作合同,因为当生成的 JUnits 运行 时,不存在数据并且它 returns 201。但是我们的消费者也想要一个 200 场景的合同.
有没有什么好的方法可以让合约执行两次相同的调用,这样我们就可以产生两种情况?
我们的合同看起来像这样(精简但基本相同):
Contract.make {
name("givenValidInputs_returnDataAndCreatedStatus")
request {
method 'PUT'
url "/path/to/resource"
headers {
contentType("application/json")
}
body(
"primaryKey": $(p('1234567890123456'), c(regex('^[0-9]+$')))
)
}
response {
status 201
body(
"generatedValue": $(p(regex('^[0-9a-zA-Z]+$')), c('abc123'))
)
}
}
这里有两件事要讨论。让我们从第一个,更重要的开始。在进行合同测试时,您不应访问数据库。如果您这样做,您不仅在测试控制器交互,而且还在测试所有其他层。在您的控制器中模拟服务,然后才 运行 合同测试。
至于第二件事 - 更改对同一请求的响应,您可以为此使用场景,也就是有状态存根。请在此处查看文档 https://cloud.spring.io/spring-cloud-static/spring-cloud-contract/2.2.1.RELEASE/reference/html/project-features.html#contract-stateful-contracts
合同测试旨在测试端点的技术握手。它不应该用于测试(有状态的)行为。 200 和 201 之间的 http 代码差异可以被认为是一种边缘情况,但在我看来是一种语义差异。
所以我同意 Marcin Grzejszczak 的观点,您应该模拟该服务。使用该模拟服务,如果您真的坚持为特定的 http 代码定义合同,则可以模拟该行为。
除了场景之外,您可以指定一个特定的主键来表示 "already exists" 场景。
唯一的问题是,当消费者在调用存根时提供“99999999”时,它将在两个合同上匹配(因为该值也与 201 合同中提供的正则表达式匹配)。
如果您只是将 "priority: 1" 添加到合同中,则在提供特定 属性 时,200 合同将优先于 201 合同。
Contract.make {
name("when put existing, expect 200")
request {
method 'PUT'
url "/path/to/resource"
headers {
contentType("application/json")
}
body(
"primaryKey": "99999999"
)
}
response {
status 200
body(
"generatedValue": $(p(regex('^[0-9a-zA-Z]+$')), c('abc123'))
)
}
priority 1
}
在生产者方面,您必须模拟您的服务才能在提供“99999999”值时正确响应。
我认为我个人不会将此 http 代码包含在合同测试中,因为它代表状态行为,因此是语义的,而不是我认为的技术连接要求。在双方,我都会在非集成单元测试中测试给定情况的行为。尽管在这种情况下有时很难将句法与语义分开。
我正在开发一个小型 REST 服务来处理一些数据并将其持久化(目前是 Oracle,致力于添加缓存)。
我们开始使用 Spring 云合同框架处理消费者驱动的合同。我们无法为我们打算如何实施该服务定义一份合同。
我们有一个 PUT 端点,它在正文中接受一个输入字段,这是我们持久化的主键。在我们的服务中,我们在数据库中查找该字段,如果存在,我们希望 return 数据库中的现有数据带有 200 状态代码,表示没有添加新记录。如果那个值还没有在数据库中,那么我们有一些逻辑来为它生成数据并将数据插入数据库中。然后,我们希望 return 201 表示数据已创建。
我们有一个 201 案例的工作合同,因为当生成的 JUnits 运行 时,不存在数据并且它 returns 201。但是我们的消费者也想要一个 200 场景的合同.
有没有什么好的方法可以让合约执行两次相同的调用,这样我们就可以产生两种情况?
我们的合同看起来像这样(精简但基本相同):
Contract.make {
name("givenValidInputs_returnDataAndCreatedStatus")
request {
method 'PUT'
url "/path/to/resource"
headers {
contentType("application/json")
}
body(
"primaryKey": $(p('1234567890123456'), c(regex('^[0-9]+$')))
)
}
response {
status 201
body(
"generatedValue": $(p(regex('^[0-9a-zA-Z]+$')), c('abc123'))
)
}
}
这里有两件事要讨论。让我们从第一个,更重要的开始。在进行合同测试时,您不应访问数据库。如果您这样做,您不仅在测试控制器交互,而且还在测试所有其他层。在您的控制器中模拟服务,然后才 运行 合同测试。
至于第二件事 - 更改对同一请求的响应,您可以为此使用场景,也就是有状态存根。请在此处查看文档 https://cloud.spring.io/spring-cloud-static/spring-cloud-contract/2.2.1.RELEASE/reference/html/project-features.html#contract-stateful-contracts
合同测试旨在测试端点的技术握手。它不应该用于测试(有状态的)行为。 200 和 201 之间的 http 代码差异可以被认为是一种边缘情况,但在我看来是一种语义差异。
所以我同意 Marcin Grzejszczak 的观点,您应该模拟该服务。使用该模拟服务,如果您真的坚持为特定的 http 代码定义合同,则可以模拟该行为。
除了场景之外,您可以指定一个特定的主键来表示 "already exists" 场景。 唯一的问题是,当消费者在调用存根时提供“99999999”时,它将在两个合同上匹配(因为该值也与 201 合同中提供的正则表达式匹配)。
如果您只是将 "priority: 1" 添加到合同中,则在提供特定 属性 时,200 合同将优先于 201 合同。
Contract.make {
name("when put existing, expect 200")
request {
method 'PUT'
url "/path/to/resource"
headers {
contentType("application/json")
}
body(
"primaryKey": "99999999"
)
}
response {
status 200
body(
"generatedValue": $(p(regex('^[0-9a-zA-Z]+$')), c('abc123'))
)
}
priority 1
}
在生产者方面,您必须模拟您的服务才能在提供“99999999”值时正确响应。
我认为我个人不会将此 http 代码包含在合同测试中,因为它代表状态行为,因此是语义的,而不是我认为的技术连接要求。在双方,我都会在非集成单元测试中测试给定情况的行为。尽管在这种情况下有时很难将句法与语义分开。