Java Pact提供者测试的范围应该是什么?
大约半年前,我的组织开始使用Pact在用Java编写的REST服务/微服务之间创建/验证契约。 我们很难决定提供商测试的适当范围或掌握程度,并且希望从其他pact用户的经验中获得一些信息 基本上,讨论围绕在提供者测试中模拟/存根的位置展开。在一个服务中,您至少必须模拟对其他服务的外部调用,但您也可以选择更接近REST资源类的模拟 我们将其归结为两种选择: 1。第一种选择是,提供者测试应该是严格的契约测试,并且只执行提供者服务的REST资源类,模拟/删除从那里使用的服务类/编排器等。该合同测试将通过组件测试进行补充,组件测试将测试由供应商测试生成/模拟的部件 2.第二个选项是使用提供者测试作为组件测试,该测试将针对每个请求执行整个服务组件。只有对其他组件的可传递外部调用才会被模拟/存根 这些是专业人士对每个选项的想法 选项1的专业版:Java Pact提供者测试的范围应该是什么?,java,unit-testing,testing,pact,pact-java,Java,Unit Testing,Testing,Pact,Pact Java,大约半年前,我的组织开始使用Pact在用Java编写的REST服务/微服务之间创建/验证契约。 我们很难决定提供商测试的适当范围或掌握程度,并且希望从其他pact用户的经验中获得一些信息 基本上,讨论围绕在提供者测试中模拟/存根的位置展开。在一个服务中,您至少必须模拟对其他服务的外部调用,但您也可以选择更接近REST资源类的模拟 我们将其归结为两种选择: 1。第一种选择是,提供者测试应该是严格的契约测试,并且只执行提供者服务的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();
}
在上面的示例中,@Autowired(我们使用spring)CustomerOrchestrator可以在运行提供者测试时被模拟,也可以注入真正的“Impl”类。如果您选择注入真正的“CustomerOrchestratorImpl.class”,它将具有额外的@autowiredbean依赖项,而这些依赖项又可能具有其他。。。最后,依赖关系将在进行数据库调用的DAO对象或对其他下游服务/组件执行HTTP调用的REST客户端中结束
如果我们在上面的示例中采用我的“选项1”解决方案,我们将模拟CustomerResource中的customerOrchestrator字段,如果我们采用“选项2”,我们将注入Impl类(实际类)对于CustomerResource依赖关系图中的每个依赖项,创建模拟数据库条目和模拟下游服务
作为旁注,我应该提到,我们很少在提供者测试中实际使用真实的数据库。在采用“选项2”的情况下,我们模拟了DAO类层,而不是模拟实际的数据库数据,以减少测试中移动部件的数量
我们已经创建了一个“测试框架”,可以自动模拟在spring上下文中未显式声明的任何自动连接依赖项,因此存根/模拟对我们来说是一个轻量级的过程。这是一个提供者测试的摘录,该测试练习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 HttpTarget(jersyTestRule.port);
private static PactJerseyWebbAppDescriptorRule buildWebAppDescriptorRule() {
return PactJerseyWebbAppDescriptorRule.Builder.getBuilder()
.withContextConfigLocation("classpath:applicationContext-test.xml")
.withRestResourceClazzes(CustomerResource.class)
.withPackages("api.rest.customer")
.build();
}
@State("that customer with id 1111111 exists")
public void state1() throws Exception {
CustomerOrchestrator customerOrchestratorStub = SpringApplicationContext.getBean(CustomerOrchestrator.class)
when(customerOrchestratorStub.getCustomer(eq("1111111"))).thenReturn(createMockedCustomer("1111111));
}
...
这是一个经常出现的问题,我的回答是“为每项服务做有意义的事情”。pact用于的第一个微服务非常小和简单,因此在没有任何模拟或存根的情况下测试整个服务是最容易的。对实际服务的调用和验证测试中的调用之间的唯一区别是我们使用sqlite进行测试。当然,我们会中断对下游服务的呼叫 如果设置真实数据比存根更复杂,那么我会使用存根然而!如果要执行此操作,则需要确保以与pact相同的方式验证存根上的调用。使用某种共享装置,并确保对于在pact提供者测试中存根的每个调用,都有一个匹配测试,以确保行为符合预期。这就像是将协作/合同测试链接在一起,如下所示:
我说选择2 原因是因为Pact的整个存在理由是对代码更改有信心——这不会破坏消费者,或者如果它破坏了消费者,找到一种方法来管理更改(版本控制),同时保持交互完好无损 要完全自信,您必须尽可能多地使用“真实”代码,尽管数据可以是模拟的或真实的,但在这一点上并不重要。请记住,您希望在部署生产代码之前能够测试尽可能多的生产代码 按照我使用它的方式,我有两种类型的测试,单元测试和Pact测试。单元测试确保我的代码不会因愚蠢的错误或错误的输入而中断,这对于模拟依赖项非常有用,而Pact测试用于测试使用者和提供者之间的交互,并且我的代码更改不会影响请求或数据格式。您可以在这里模拟依赖关系,但这可能会在生产中留下一些漏洞,因为该依赖关系可能会影响请求或数据 不过,最终还是取决于您如何使用Pact,只要您使用它来测试o