我目前正在开发一个项目,我们在微服务架构中实现了kafka.您是否成功使用pact-jvm创建mS到kafka主题交互的合同测试用例?
我的实现是microservice1向REST客户端发布消息,然后REST客户端将消息发布到Kafka主题.microservice2使用GET方法从Kafka主题中检索消息.
大约半年前,我的组织开始使用Pact来创建/验证用Java编写的REST服务/微服务之间的合同.我们很难决定提供商测试的适当范围或把握应该是什么,并且会喜欢其他协议用户的经验.
基本上,讨论围绕提供程序测试中的mock/stub的位置进行演变.在服务中,您必须至少模拟对其他服务的外部调用,但您也可以选择模拟更接近REST资源类.
我们将其归结为两种选择:
1.第一个选项是提供者测试应该是严格的合同测试,并且只运用提供者服务的REST资源类,模拟/删除从那里使用的服务类/协调器等.此合同测试将通过组件测试进行扩充,该组件测试将测试由提供程序测试存根/模拟的部件.
2.第二个选项是使用提供程序测试作为组件测试,该测试将为每个请求执行整个服务组件.只有对其他组件的传递外部调用才会被模拟/存根.
这些是每个选项的专业人士的想法
选项1的专业版:
选项2的专业版:
我真的很想知道您的提供商测试通常在这方面的看法.有最好的做法吗?
澄清"组件"的含义:组件是微服务或更大服务应用程序中的模块.我们采用了来自Martin Fowlers的组件的定义http://martinfowler.com/articles/microservice-testing/.
提供者服务/组件通常在Jersey资源类中具有REST端点.此端点是Pact提供程序测试的提供程序端点.一个例子:
@Path("/customer")
public class CustomerResource {
@Autowired private CustomerOrchestrator customerOrchestrator;
@GET
@Path("/{customerId}")
@Produces(MediaType.APPLICATION_JSON)
public Response get(@PathParam("customerId") String id) {
CustomerId customerId = CustomerIdValidator.validate(id);
return Response.ok(toJson(customerOrchestrator.getCustomer(customerId))).build();
}
Run Code Online (Sandbox Code Playgroud)
在上面的示例中,@ Aututired(我们使用spring)CustomerOrchestrator可以在运行提供程序测试时进行模拟,也可以注入真正的"Impl"类.如果您选择注入真正的"CustomerOrchestratorImpl.class",它将具有额外的@Autowired bean依赖项,而这些依赖项又可能具有其他...等等.最后,依赖项将最终出现在将进行数据库调用的DAO对象中,或者一个REST客户端,它将对其他下游服务/组件执行HTTP调用.
如果我们在上面的例子中采用我的"选项1"解决方案,我们将模拟CustomerResource中的customerOrchestrator字段,如果我们采用"选项2",我们将为CustomerResource中的每个依赖项注入Impl类(真实类)依赖图并创建模拟的数据库条目和模拟下游服务.
作为旁注,我应该提到我们很少在提供程序测试中实际使用真实数据库.在我们采用"选项2"的情况下,我们模拟了DAO类层,而不是模拟实际的数据库数据,以减少测试中移动部件的数量.
我们创建了一个"测试框架",可以自动模拟任何未在spring上下文中显式声明的Autowired依赖项,因此stubing/mocking对我们来说是一个轻量级的过程.这是运行CustomerResource并启动存根的CustomerOrchestrator bean的提供程序测试的摘录:
@RunWith(PactRunner.class)
@Provider("customer-rest-api")
@PactCachedLoader(CustomerProviderContractTest.class)
public class CustomerProviderContractTest {
@ClassRule
public static PactJerseyWebbAppDescriptorRule webAppRule = buildWebAppDescriptorRule();
@Rule
public PactJerseyTestRule jersyTestRule = new PactJerseyTestRule(webAppRule.appDescriptor);
@TestTarget public final Target target = new …Run Code Online (Sandbox Code Playgroud) 我正在寻找一种使用模拟提供程序启动 NestJS 应用程序的方法。这对于提供者合同测试是必要的,因为需要单独启动服务。使用 Pact 库,测试提供者假定提供者服务已经在运行。它需要能够针对实际服务器发出 HTTP 请求(如有必要,可以模拟一些依赖项)。契约JS
我查看了 NestJS 的文档,我能找到的最接近的解决方案粘贴在下面。据我所知,这个解决方案告诉模块CatsService用catsService. 这理论上适用于提供者合同测试目的,但我认为这不允许启动整个应用程序,只是一个模块。文档中没有提到能够使用测试模块在特定端口上启动应用程序。我试图调用app.listen返回的应用程序对象,但它未能命中调用后立即放置的断点。
import * as request from "supertest";
import { Test } from "@nestjs/testing";
import { CatsModule } from "../../src/cats/cats.module";
import { CatsService } from "../../src/cats/cats.service";
import { INestApplication } from "@nestjs/common";
describe("Cats", () => {
let app: INestApplication;
let catsService = { findAll: () => ["test"] };
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [CatsModule]
})
.overrideProvider(CatsService)
.useValue(catsService) …Run Code Online (Sandbox Code Playgroud) 我正在尝试在我们公司引入 Pact 框架,提出的问题之一如下:
场景:这个 xyz API 被 40 个消费者调用,每个消费者当前都需要相同的功能。那么为什么我们应该维护 40 个 Pact 文件而不是只维护一个文件呢?
考虑到契约文件的维护,是否有比为每个消费者拥有一个契约文件更好的方法?
是否有一个小而简洁的 jvm 消费者 Java(消费者或提供者)junit 项目可供某人共享,最好是在一个自包含的 zip 文件中,其中包含除 jar 导入之外的所有文件?
当我按照此处的步骤进行操作时: https: //github.com/DiUS/pact-jvm/tree/master/pact-jvm-consumer-junit和“使用基本 ConsumerPactTest”时,我遇到了各种错误,我已经尝试过来一一解决。
然而我相信一定存在一个更简单的“Hello world”类型的示例来描述这个框架。
我之前已经设置过其他微服务模拟框架,但是 Pact 在设置它时显示了太多错误,所以我认为一定是误解了它应该如何以最简单的形式进行设置。
我在上述示例中遇到的最后一个问题是这些导入,我发现没有 jar 文件:
import au.com.dius.pact.consumer.exampleclients.ConsumerClient;
import au.com.dius.pact.consumer.ConsumerPactTest;
import au.com.dius.pact.model.PactFragment;
因此,例如 @Pact 关键字无法解析等。
提前致谢!
更新 - 在创建契约定义时(使用 ./gradlew test ),是否可以不使用契约提供者模拟服务器代码,而是使用“真正的”api 提供者?
例如,在 1) 的示例中,是否可以使用“真实”api 提供者响应来更新(例如使用邮递员中记录的响应)消费者的 @Pact 部分,而不需要在spring boot localhost 模拟服务器?
1)http://the-creative-tester.github.io/Java-Consumer-Driven-Contract-Testing/
感谢您的任何答复!
我正在使用 pactNet 来测试一个 API,它应该返回一个灵活长度的数组。
如果我调用“myApi/items/”,它应该返回一个消费者不知道确切尺寸的项目列表。所以答案应该是这样的:
[
{
"id": "1",
"description": "foo"
},
{
"id": "2",
"description": "foo2"
},
{
"id": "3",
"description": "foo3"
}
]
Run Code Online (Sandbox Code Playgroud)
或这个:
[
{
"id": "4",
"description": "foo4"
},
{
"id": "2",
"description": "foo2"
}
]
Run Code Online (Sandbox Code Playgroud)
如何为此交互创建合同?
在文档是在Ruby中的例子,但是我无法找到C#中的等价物。
我正在使用 pactNet 2.1.1 版。
编辑:这是一个示例,它应该是什么样子。我想知道的是如何声明主体应该包含一个长度灵活的项目数组。
[Test]
public void GetAllItems()
{
//Arrange
_mockProviderService
.Given("There are items")
.UponReceiving("A GET request to retrieve the items")
.With(new ProviderServiceRequest
{
Method = HttpVerb.Get,
Path = "/items/",
Headers = new Dictionary<string, object> …Run Code Online (Sandbox Code Playgroud) 我不明白断言在@PactVerification. 对我来说,这更像是一种复杂的说法1 == 1。例如:
import static org.assertj.core.api.Assertions.assertThat;
public class PactConsumerDrivenContractUnitTest {
@Rule
public PactProviderRuleMk2 mockProvider
= new PactProviderRuleMk2("test_provider", "localhost", 8080, this);
@Pact(consumer = "test_consumer")
public RequestResponsePact createPact(PactDslWithProvider builder) {
return builder
.given("test GET ")
.uponReceiving("GET REQUEST")
.path("/")
.method("GET")
.willRespondWith()
.body("{\"condition\": true, \"name\": \"tom\"}")
}
@Test
@PactVerification()
public void givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody() {
//when
ResponseEntity<String> response
= new RestTemplate().getForEntity(mockProvider.getUrl(), String.class);
//then
assertThat(response.getBody()).contains("condition", "true", "name", "tom");
}
}
Run Code Online (Sandbox Code Playgroud)
所以首先在“createPact”中我们声明
body("{\"condition\": true, \"name\": \"tom\"}")
Run Code Online (Sandbox Code Playgroud)
然后在givenGet_whenSendRequest_shouldReturn200WithProperHeaderAndBody注释中@PactVerification我们这样做
assertThat(response.getBody()).contains("condition", "true", …Run Code Online (Sandbox Code Playgroud) 我试图了解PACT和Spring Cloud Contract之间更好的工具,以实施消费者驱动程序合同测试。我没有找到任何明显的例子来寻找利弊。
我想实现CDCT,并且在我的项目中不使用Spring。根据我的理解,我认为PACT很好。
欢迎任何信息或建议。谢谢。
我正在为上传文件的放置请求设置测试。我的pact文件中的请求正文包含一个字符串,其中包含一个mime边界,该边界在每次测试运行时都会改变。我正在尝试为请求正文字符串定义一个正则表达式匹配规则,但是不匹配。标头content-type的相似匹配规则确实匹配。
如果主体只是一个字符串,应该如何为主体定义匹配规则?
我正在Rust中使用Pact的参考实现。契约规范版本为3。
"request": {
"headers": {
"Content-Length": "206",
"Host": "127.0.0.1:1234",
"Connection": "Close",
"Content-Type": "multipart/form-data; boundary=\"MIME_boundary_4FBA8D0826C707B6\""
},
"body": "--MIME_boundary_4FBA8D0826C707B6\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test_file.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nContent of test file.\r\n--MIME_boundary_4FBA8D0826C707B6--\r\n",
"matchingRules": {
"header": {
"$.Content-Type": {
"matchers": [
{
"match": "regex",
"regex": "multipart/form-data; boundary=\"MIME_boundary_[A-Z0-9]{16}\""
}
]
}
},
"body": {
"$": {
"matchers": [
{
"match": "regex",
"regex": "--MIME_boundary_[A-Z0-9]{16}\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test_file.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nContent of test file.\r\n--MIME_boundary_[A-Z0-9]{16}--\r\n"
}
]
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
上面的代码是测试中使用的pact文件的一部分。测试导致BodyMismatch错误。比较预期和接收的主体表明它们的mime边界不同,因此正则表达式匹配不起作用。
我目前正在研究 Pact,作为测试策略开发的一部分。它是一个微服务架构,并且有各种服务器到服务器的连接,我可以看到它非常有用(包括消息队列)。
然而,我无法准确理解它应该如何工作的一个地方是客户端和服务器之间的连接。在我们最常见的模式中,我们有一个 Java 微服务,充当 Typescript/Angular Web 客户端的服务器。服务器采用OpenAPI规范;具体来说,我们手动编写 OpenAPI 规范文件,然后从规范文件生成服务器和客户端代码 - 服务器代码是我们期望实现的控制器的一系列接口,客户端代码是服务和模型库客户端可以使用它向服务器发出请求。客户端上的这种模式使 HTTP 请求变得轻而易举,原因如下:
一方面,在此设置中使用 Pact 绝对可以带来一些好处:
另一方面,我也有一些担忧:
坦率地说,仅第一个积极因素就足以让我承诺加入 Pact。消费者驱动的方法使得生成存根的过程变得更加有意义。话虽这么说,负面影响肯定会让我感到厌烦。感觉工作量很大,其中大部分是引入冗余验证机制,这样我们就可以获取单一利益。
我这样做是错误的吗?我是否可以对这种方法进行简单的改变,以获得相同的好处而不引入冗余?或者我只需要接受这就是方式?
编辑:所以我开始研究使用契约生成存根服务器的工具,结果发现它非常缺乏。内置的 pact 服务器存根不支持以编程方式向正在运行的服务器添加模拟,而且我发现将 pact 转换为与其他服务器存根库一起使用的大多数库都非常小,并且维护得不是特别好。这意味着我们可能必须为存根过程构建我们自己的解决方案,这使得 Pact 的吸引力更小=/
pact ×10
java ×4
consumer ×2
jvm ×2
apache-kafka ×1
api ×1
c# ×1
contract ×1
javascript ×1
junit ×1
nestjs ×1
node.js ×1
openapi ×1
pact-java ×1
pact-jvm ×1
pact-net ×1
testing ×1
typescript ×1
unit-testing ×1