优秀的编程知识分享平台

网站首页 > 技术文章 正文

如何使用Spring Cloud Contract(如何使用朋友的山姆卡)

nanyue 2024-10-17 11:15:50 技术文章 11 ℃

使用Spring Cloud Contract

Spring Cloud Contract提供的契约测试方法与Pack略有不同。在Pack中,使用者负责发布契约,在Spring Cloud Contract 中,此操作的发起者却是提供者。契约作为JAR存储在Maven存储库中,包含基于契约定义文件自动生成的桩。可以使用Groovy DSL语法创建这些定义。它们中的每一个都包含两个主要部分:请求和响应规范。在这些文件的基础上, Spring Cloud Contract生成JSON桩定义,WireMock 使用它们在客户端进行集成测试。与Pact (用作支持REST API的使用者驱动的契约测试的工具)相比,它专门用于测试基于JVM的微服务。它由以下3个子项目组成。

口Spring Cloud Contract Verifier

口Spring Cloud Contract Stub Runner

口Spring Cloud Contract WireMock

现在我们不妨来分析如何在契约测试中使用它们,为方便起见,测试仍然使用之前在前文中“使用Pact”中描述的相同示例。

?注意:

WireMock是基于HTTP的API的模拟器。有些开发人员可能会认为它是服务虚拟化工具或模拟服务器。通过捕获流入和流出现有API的流量,它能够快速启动和运行。

1.定义契约和生成桩

如前文所述,与Pact相反,在Spring Cloud Contract中,提供者(服务器端)负责发布契约规范。因此,我们将从account-service 服务开始实现,该服务为customer-service服务调用的端点提供服务。但在继续实现之前,请看图13.6 中的图解,它说明了参与测试过程的主要组件。

示例应用程序的源代码在与先前示例相同的GitHub存储库中可用,但在不同的contract分支上。

要为提供者端应用程序启用Spring Cloud Contract 功能,首先必须将Spring Cloud Contract Verifier包含在项目依赖项中。

<dependency>

<groupId>org . springf ramework. cloud</groupId>

<artifactId>spring-cloud- starter-contract-verifier</artifactid>

<scope>test</scope>

</ dependency>

下一步是添加Spring Cloud Contract Verifer Maven插件,该插件将生成并运行契约测试。它还会在本地Maven存储库中生成和安装桩。它有一个必须定义的唯一参数,即由生成的测试类扩展的基类所在的包(Package)。

<plugin>

<groupId>org. spr ingframework. cloud</groupId>

<arti factId>spring-cloud-contract-maven-plugin</artifactId>

<version>1.2.0. RELEASE</version>

<extensions>true</extensions>

<configuration>

<packageWi thBaseClasses>pl .piomin. services . account</packageNi thBaseClasses>

</configuration>

</plugin>

现在,我们必须为契约测试创建一个基类。它应该放在pl.piomin.services.account包中。在下面的基类中,将使用@SpringBooTest 设置Spring Boot 应用程序,然后模拟AccountRepository.开发人员还可以使用RestAssured来模拟Spring MVC,并仅向控制器发送请求。由于上述模拟的存在,测试不会与任何外部组件(如数据库或HTTP端点)交互,并仅测试契约。

@RunWith (SpringRunner .class)

@SpringBootTest (classes = {AccountApplication.class})

public abstract class AccountProviderTestBase {

@Autowired

private WebAppl icationContext context;

@MockBean

private AccountRepository repository;

@Before

public void setup() {

Res tAssuredMockMvc . webAppContextSetup (context) ;

List<Account> accounts 一new ArrayList<>() ;

accounts .add (new Account ("1","123", 5000, "1"));

accounts. add (new Account ("2", "124", 5000, "1"));

accounts. add (new Account ("3", "125", 5000, "1")) ;

when (repository. findByCustomerId("I")) . thenReturn (accounts) ;

}

}

我们已经提供了使用Spring Cloud Contract运行测试所需的所有配置和基类。因此,现在可以进入最重要的部分,使用Spring Cloud Contract Groovy DSL定义契约。契约的所有规范都应位于/src/test/resources/contracts目录中。此目录下的特定位置(包含桩定义)将被视为基本测试类名。每个桩定义代表单个契约测试。根据此规则,spring cloud-contract- maven-plugin会自动查找契约并将其分配给基础测试类。在目前讨论的示例中,我们将桩定义放在/src/test/resources/contracts/accountService目录中。因此,生成的测试类名称为AccountServiceTest,它还扩展了AccountServiceBase 类。

以下是契约规范的示例,它返回属于客户的账户列表。这份契约不是很简单,所以有些事情需要解释。开发人员可以使用正则表达式在Contract DSL 中编写请求。还可以根据通信方(使用者或生产者)为每个属性提供不同的值。Contract DSL还使开发人员能够使用fromRequest方法在响应中引用请求。以下契约将返回3个账户的列表,从请求路径和id字段中获取customerld字段,其中包含5个数字。

org.springframework. cloud. contract. spec. Contract .make {

request {

method ' GET '

url value (consumer (regex(' /customer/[0-9](3}')), producer(' /customer/1'1)

response {

status 200

body([

id: $(regex('[0-9]{5}')),

number: '123',

balance: 5000,

customerId: fromRequest() .path(1)

id: $(regex(' [0-9]{5)')),

number: '124',

balance: 5000,

customerId: fromRequest() .path(1)

] , [

id: $(regex('[0-9](5}')),

number: '125' ,

balance: 5000,

customerId: fromRequest () .path(1)

]

] )

headers {

contentType (appl icationJson( ) )

}

}

}

在Maven构建的测试阶段,在target/generated-test- sources目录下将生成测试类。以下是从上述契约规范中生成的类。

public class AccountServiceTest extends AccountServiceBase {

@Test

public void validate_ customerContract () throws Exception

// given:

Moc kMvcRequestSpecification request = given() ;

// when:

ResponseOptions response = given() .spec (request)

.get ("/customer/1");

// then:

assertThat (response . statusCode()) . isEqualTo(200) ;

assertThat (response . header ("Content-

Type")) . matches ("application/json.*");

// and:

DocumentContext parsedJson =

JsonPath. parse (response .getBody() . asString));

assertThatJson (parsedJson) .array() .contains ("I'number']")。isEqualTo("123");

assertThatJson (parsedJson) ,array() . contains ("[ 'balance ']”) . isEqualTo (5000)0 ;

assertThatJson (parsedJson) . array() .contains ("['number']") . isEqualTo("124");

assertThatJson (parsedJson) .array() .contains ("[' customerId']"). isEqualTo ("1");

assertThatJson (parsedJson) . array() .contains ("['id']") .matches ("[0-9](5}");

}

}

2.验证使用者方面的契约

假设我们已经在提供者端成功构建并运行了测试,那么将生成桩,然后在本地Maven存储库中发布。为了能够在使用者应用程序测试期间使用它们,应该将Spring Cloud Contract Stub Runner包含到项目依赖项中。

<dependency>

<groupId>org . spr ingframework. cloud</groupId>

<arti factId>spring-cloud-starter-contract-stub- runner</artifactId>

<scope>test</ scope>

</ dependency>

然后,我们应该用@AutoConfigureStubRunner 注解测试类。它需要两个输入参数一ids和workOffline. ids 字段是artifactld、groupld、 版本号、stubs 限定符和端口号的连接产物,并且通常指向提供者发布的桩的JAR。worfkline 标志指示具有桩的存储库所在的位置。默认情况下,使用者将尝试从Nexus或Artifactory 自动下载工件。如果开发人员希望强制Spring Cloud Contract Stub Runner仅从本地Maven存储库下载桩,则可以将workOfline参数的值切换为true.

以下是一个JUnit测试类,它使用Feign客户端从提供者端发布的桩中调用端点。Spring Cloud Contract会查找pl.piomin.seriesaccount-service工件的最新版本。已经通过在@AutoConfigureStubRunner注解中将+符号作为桩的版本传递来表示。如果开发人员想要使用该工件的具体版本,则可以从pom.xml文件而不是使用+符号来设置当前版本,如@AutoConfigureStubRunnerids= {plpiomin.sceicsacount-service:1.0-SNAPSHOT:stubs:8091" })。

@RunWith (SpringRunner. class)

@SpringBootTest (properties = {

"eureka.client.enabled: false"

@AutoConfigureStubRunner(ids = {"pl.piomin.services :account-

service:+:stubs:8091"},workOffline = true)

public class AccountContractTest {

@Autowi red

private AccountClient accountClient;

@Test

public void verifyAccounts() {

List<Account> accounts = accountClient. findByCustomer ("1");

Assert.assertEquals(3, accounts.size( ) ) ;

现在剩下的唯一事情是使用mvncleaninstall命令构建整个项目,以验证测试是否成功运行。但是,开发人员应该记住的是,之前创建的测试仅涵盖customer-service服务和account-service服务之间的集成。在我们的示例系统中,应该验证的微服务之间还有一些其他的集成。我们将演示一个测试整个系统的示例。它将测试已公开的order-service服务的方法,并且该服务将与所有其他微服务进行通信。为此,我们将使用Spring Cloud Contract方案的另一个有趣功能。

3.方案

使用Spring Cloud Contract定义方案( Scenario)并不困难。唯一需要做的就是在创建契约时提供正确的命名约定。此约定假定作为方案一部分的每个契约的名称都以订单号和下画线为前缀。单个方案中包含的所有契约都必须位于同一目录中。Spring Cloud Contract方案基于WireMock的方案。以下是一个目录结构,其中包含为创建和接受订单的方案需求定义的合约。

src \main \ resources \contracts

orderService \

1_ createorder .groovy

2_ acceptOrder .groovy

以下是为该方案生成的测试的源代码。

@FixMe thodorder (MethodSorters . NAME ASCENDING)

public class OrderScenarioTest extends OrderScenarioBase {

@Test

public void validate_ 1 createOrder() throws Exception {

/ /...

}

@Test

public void validate_ 2 acceptOrder() throws Exception {

/ / ...

}

}

现在,假设我们有很多微服务,并且大多数微服务与一个或多个其他微服务进行通信。因此,即使测试单个契约,也无法确保在服务间通信期间所有其他契约按预期工作。但是,使用Spring Cloud Contract,则可以轻松地将所有必需的桩包含在测试类中,这使得开发人员能够验证已定义方案中的所有契约。这需要包含spring-cloud-starter- contract-verifier和spring: cloud-starter contract-stub runner依赖项。以下类定义将充当Spring Cloud Contract测试类的基础,并包括由其他微服务生成的桩。为order-service服务端点生成的桩可以由需要使用order- service服务验证契约的任何其他外部服务使用。像以下代码这样的测试不仅将验证此服务与order-service服务之间的契约,还将验证order-service服务与该服务使用的其他服务之间的契约。

@RunWith (Spr ingRunner.class)

@SpringBootTest (properties = {

"eureka.client.enabled: false"

} )

@AutoConfigureStubRunner(ids =

"pl.piomin.services:account-service:t:stubs:8091",

"pl.pi omin. services:customer -service:+:stubs :8092",

"pl.piomin. services :product-service: +:stubs:8093"

}, workOffline 一true)

public class OrderScenarioBase {

@Autowi red

private WebApplicationContext context;

@MockBean

private OrderRepository repository;

@Before

public void setup() {

RestAssuredMockMvc . webAppContextSetup (context) ;

when (repository. countByCustomerId (Matchers .anystring()))。

thenReturn(0) ;

when (repository. save (Mockito.any (Order.class)))。thenAnswer (new

Answer<Order>() (

@override

public Order answer (InvocationOnMock invocation) throws

Throwable{

Order. - invocation. getArgumentAt(0, Order.class) ;

o.setId("12345") ;

return Q;

}

} );

}

}


本文给大家讲解的内容是如何使用Spring Cloud Contract

1.下篇文章给大家讲解的是性能测试;

2.觉得文章不错的朋友可以转发此文关注小编,有需要的可以私信小编获取资料;

3.感谢大家的支持!

Tags:

最近发表
标签列表