SpringCloud 消费者方(发放贷款)

2023-12-13 09:50 更新

作为贷款发行服务的开发人员(欺诈检测服务器的使用者),您可以执行以下步骤:

  1. 通过为您的功能编写测试来开始进行TDD。
  2. 编写缺少的实现。
  3. 在本地克隆欺诈检测服务存储库。
  4. 在欺诈检测服务的仓库中本地定义合同。
  5. 添加Spring Cloud Contract验证程序插件。
  6. 运行集成测试。
  7. 提出拉取请求。
  8. 创建一个初始实现。
  9. 接管请求请求。
  10. 编写缺少的实现。
  11. 部署您的应用程序。
  12. 在线工作。

通过为您的功能编写测试来开始进行TDD。

@Test
public void shouldBeRejectedDueToAbnormalLoanAmount() {
	// given:
	LoanApplication application = new LoanApplication(new Client("1234567890"),
			99999);
	// when:
	LoanApplicationResult loanApplication = service.loanApplication(application);
	// then:
	assertThat(loanApplication.getLoanApplicationStatus())
			.isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED);
	assertThat(loanApplication.getRejectionReason()).isEqualTo("Amount too high");
}

假设您已经编写了新功能的测试。如果收到大量贷款申请,则系统应拒绝该贷款申请并提供一些说明。

编写缺少的实现。

在某个时间点,您需要向欺诈检测服务发送请求。假设您需要发送包含客户ID和客户希望借入的金额的请求。您想通过PUT方法将其发送到/fraudcheck网址。

ResponseEntity<FraudServiceResponse> response = restTemplate.exchange(
		"http://localhost:" + port + "/fraudcheck", HttpMethod.PUT,
		new HttpEntity<>(request, httpHeaders), FraudServiceResponse.class);

为简单起见,欺诈检测服务的端口设置为8080,应用程序在8090上运行。

如果此时开始测试,则会中断测试,因为当前没有服务在端口8080上运行。

在本地克隆欺诈检测服务存储库。

您可以从服务器端合同开始。为此,您必须首先克隆它。

$ git clone https://your-git-server.com/server-side.git local-http-server-repo

在欺诈检测服务的仓库中本地定义合同。

作为消费者,您需要定义要实现的目标。您需要制定自己的期望。为此,请编写以下合同:

 将合同放在src/test/resources/contracts/fraud文件夹下。fraud文件夹很重要,因为生产者的测试基类名称引用了该文件夹。

Groovy DSL。 

/*
 * Copyright 2013-2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package contracts

org.springframework.cloud.contract.spec.Contract.make {
	request { // (1)
		method 'PUT' // (2)
		url '/fraudcheck' // (3)
		body([ // (4)
			   "client.id": $(regex('[0-9]{10}')),
			   loanAmount : 99999
		])
		headers { // (5)
			contentType('application/json')
		}
	}
	response { // (6)
		status OK() // (7)
		body([ // (8)
			   fraudCheckStatus  : "FRAUD",
			   "rejection.reason": "Amount too high"
		])
		headers { // (9)
			contentType('application/json')
		}
	}
}

/*
From the Consumer perspective, when shooting a request in the integration test:

(1) - If the consumer sends a request
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
 * has a field `client.id` that matches a regular expression `[0-9]{10}`
 * has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the response will be sent with
(7) - status equal `200`
(8) - and JSON body equal to
 { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` equal to `application/json`

From the Producer perspective, in the autogenerated producer-side test:

(1) - A request will be sent to the producer
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
 * has a field `client.id` that will have a generated value that matches a regular expression `[0-9]{10}`
 * has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the test will assert if the response has been sent with
(7) - status equal `200`
(8) - and JSON body equal to
 { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` matching `application/json.*`
 */

YAML。 

request: # (1)
  method: PUT # (2)
  url: /fraudcheck # (3)
  body: # (4)
    "client.id": 1234567890
    loanAmount: 99999
  headers: # (5)
    Content-Type: application/json
  matchers:
    body:
      - path: $.['client.id'] # (6)
        type: by_regex
        value: "[0-9]{10}"
response: # (7)
  status: 200 # (8)
  body:  # (9)
    fraudCheckStatus: "FRAUD"
    "rejection.reason": "Amount too high"
  headers: # (10)
    Content-Type: application/json;charset=UTF-8


#From the Consumer perspective, when shooting a request in the integration test:
#
#(1) - If the consumer sends a request
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `client.id`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
#(7) - then the response will be sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json`
#
#From the Producer perspective, in the autogenerated producer-side test:
#
#(1) - A request will be sent to the producer
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `client.id` `1234567890`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(7) - then the test will assert if the response has been sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`

YML合同很简单。但是,当您查看使用静态类型的Groovy DSL编写的合同时-您可能会怀疑value(client(…​), server(…​))部分是什么。通过使用此表示法,Spring Cloud Contract使您可以定义JSON块,URL等动态的部分。如果是标识符或时间戳,则无需对值进行硬编码。您要允许一些不同的值范围。要启用值范围,可以为使用者方设置与这些值匹配的正则表达式。您可以通过地图符号或带插值的字符串来提供主体。有关更多信息请参见Contract DSL部分。我们强烈建议您使用地图符号!

 您必须了解地图符号才能设置合同。请阅读有关JSON Groovy文档

前面显示的合同是双方之间的协议,其中:

  • 如果HTTP请求与所有

    • /fraudcheck端点上的PUT方法,
    • 一个具有client.id且与正则表达式[0-9]{10}loanAmount等于99999匹配的JSON正文,
    • 和值为application/vnd.fraud.v1+jsonContent-Type标头,
  • 然后将HTTP响应发送给使用者

    • 状态为200
    • 包含JSON正文,其fraudCheckStatus字段包含值FRAUD,而rejectionReason字段包含值Amount too high
    • 还有一个Content-Type标头,其值为application/vnd.fraud.v1+json

一旦准备好在集成测试中实际检查API,就需要在本地安装存根。

添加Spring Cloud Contract验证程序插件。

我们可以添加Maven或Gradle插件。在此示例中,您将了解如何添加Maven。首先,添加Spring Cloud Contract BOM。

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>${spring-cloud-release.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

接下来,添加Spring Cloud Contract Verifier Maven插件

<plugin>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-contract-maven-plugin</artifactId>
	<version>${spring-cloud-contract.version}</version>
	<extensions>true</extensions>
	<configuration>
		<packageWithBaseClasses>com.example.fraud</packageWithBaseClasses>
		<convertToYaml>true</convertToYaml>
	</configuration>
</plugin>

自从添加了插件以来,您将获得Spring Cloud Contract Verifier功能,这些功能来自提供的合同:

  • 生成并运行测试
  • 制作并安装存根

您不想生成测试,因为作为消费者,您只想玩存根。您需要跳过测试的生成和执行。执行时:

$ cd local-http-server-repo
$ ./mvnw clean install -DskipTests

在日志中,您会看到以下内容:

[INFO] --- spring-cloud-contract-maven-plugin:1.0.0.BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar
[INFO]
[INFO] --- maven-jar-plugin:2.6:jar (default-jar) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:1.5.5.BUILD-SNAPSHOT:repackage (default) @ http-server ---
[INFO]
[INFO] --- maven-install-plugin:2.5.2:install (default-install) @ http-server ---
[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.jar
[INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT.pom
[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar

以下行非常重要:

[INFO] Installing /some/path/http-server/target/http-server-0.0.1-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar

它确认http-server的存根已安装在本地存储库中。

运行集成测试。

为了从自动存根下载的Spring Cloud Contract Stub Runner功能中受益,您必须在用户端项目(Loan Application service)中执行以下操作:

添加Spring Cloud Contract BOM:

<dependencyManagement>
	<dependencies>
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>spring-cloud-dependencies</artifactId>
			<version>${spring-cloud-release-train.version}</version>
			<type>pom</type>
			<scope>import</scope>
		</dependency>
	</dependencies>
</dependencyManagement>

将依赖项添加到Spring Cloud Contract Stub Runner

<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-contract-stub-runner</artifactId>
	<scope>test</scope>
</dependency>

@AutoConfigureStubRunner注释测试类。在注释中,为Stub Runner提供group-idartifact-id,以下载合作者的存根。(可选步骤)由于您是与离线协作者一起玩,因此您还可以提供离线工作切换(StubRunnerProperties.StubsMode.LOCAL)。

@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@AutoConfigureStubRunner(ids = {
		"com.example:http-server-dsl:+:stubs:6565" }, stubsMode = StubRunnerProperties.StubsMode.LOCAL)
public class LoanApplicationServiceTests {

现在,当您运行测试时,您将看到类似以下的内容:

2016-07-19 14:22:25.403  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Desired version is + - will try to resolve the latest version
2016-07-19 14:22:25.438  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved version is 0.0.1-SNAPSHOT
2016-07-19 14:22:25.439  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolving artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT using remote repositories []
2016-07-19 14:22:25.451  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Resolved artifact com.example:http-server:jar:stubs:0.0.1-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar
2016-07-19 14:22:25.465  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/0.0.1-SNAPSHOT/http-server-0.0.1-SNAPSHOT-stubs.jar]
2016-07-19 14:22:25.475  INFO 41050 --- [           main] o.s.c.c.stubrunner.AetherStubDownloader  : Unpacked file to [/var/folders/0p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
2016-07-19 14:22:27.737  INFO 41050 --- [           main] o.s.c.c.stubrunner.StubRunnerExecutor    : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:0.0.1-SNAPSHOT:stubs=8080}]

此输出意味着Stub Runner找到了您的存根,并为您的应用启动了服务器,其组ID为com.example,工件ID为​​http-server,存根的版本为0.0.1-SNAPSHOT,且分类器为stubs端口8080

提出拉取请求。

到目前为止,您所做的是一个迭代过程。您可以试用合同,将其安装在本地,然后在用户端工作,直到合同按您的意愿运行。

对结果满意并通过测试后,将拉取请求发布到服务器端。目前,消费者方面的工作已经完成。


以上内容是否对您有帮助:
在线笔记
App下载
App下载

扫描二维码

下载编程狮App

公众号
微信公众号

编程狮公众号