SpringCloud 消費(fèi)者方(發(fā)放貸款)

2023-12-13 09:50 更新

作為貸款發(fā)行服務(wù)的開發(fā)人員(欺詐檢測(cè)服務(wù)器的使用者),您可以執(zhí)行以下步驟:

  1. 通過為您的功能編寫測(cè)試來開始進(jìn)行TDD。
  2. 編寫缺少的實(shí)現(xiàn)。
  3. 在本地克隆欺詐檢測(cè)服務(wù)存儲(chǔ)庫。
  4. 在欺詐檢測(cè)服務(wù)的倉庫中本地定義合同。
  5. 添加Spring Cloud Contract驗(yàn)證程序插件。
  6. 運(yùn)行集成測(cè)試。
  7. 提出拉取請(qǐng)求。
  8. 創(chuàng)建一個(gè)初始實(shí)現(xiàn)。
  9. 接管請(qǐng)求請(qǐng)求。
  10. 編寫缺少的實(shí)現(xiàn)。
  11. 部署您的應(yīng)用程序。
  12. 在線工作。

通過為您的功能編寫測(cè)試來開始進(jìn)行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");
}

假設(shè)您已經(jīng)編寫了新功能的測(cè)試。如果收到大量貸款申請(qǐng),則系統(tǒng)應(yīng)拒絕該貸款申請(qǐng)并提供一些說明。

編寫缺少的實(shí)現(xiàn)。

在某個(gè)時(shí)間點(diǎn),您需要向欺詐檢測(cè)服務(wù)發(fā)送請(qǐng)求。假設(shè)您需要發(fā)送包含客戶ID和客戶希望借入的金額的請(qǐng)求。您想通過PUT方法將其發(fā)送到/fraudcheck網(wǎng)址。

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

為簡(jiǎn)單起見,欺詐檢測(cè)服務(wù)的端口設(shè)置為8080,應(yīng)用程序在8090上運(yùn)行。

如果此時(shí)開始測(cè)試,則會(huì)中斷測(cè)試,因?yàn)楫?dāng)前沒有服務(wù)在端口8080上運(yùn)行。

在本地克隆欺詐檢測(cè)服務(wù)存儲(chǔ)庫。

您可以從服務(wù)器端合同開始。為此,您必須首先克隆它。

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

在欺詐檢測(cè)服務(wù)的倉庫中本地定義合同。

作為消費(fèi)者,您需要定義要實(shí)現(xiàn)的目標(biāo)。您需要制定自己的期望。為此,請(qǐng)編寫以下合同:

 將合同放在src/test/resources/contracts/fraud文件夾下。fraud文件夾很重要,因?yàn)樯a(chǎn)者的測(cè)試基類名稱引用了該文件夾。

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合同很簡(jiǎn)單。但是,當(dāng)您查看使用靜態(tài)類型的Groovy DSL編寫的合同時(shí)-您可能會(huì)懷疑value(client(…?), server(…?))部分是什么。通過使用此表示法,Spring Cloud Contract使您可以定義JSON塊,URL等動(dòng)態(tài)的部分。如果是標(biāo)識(shí)符或時(shí)間戳,則無需對(duì)值進(jìn)行硬編碼。您要允許一些不同的值范圍。要啟用值范圍,可以為使用者方設(shè)置與這些值匹配的正則表達(dá)式。您可以通過地圖符號(hào)或帶插值的字符串來提供主體。有關(guān)更多信息,請(qǐng)參見Contract DSL部分。我們強(qiáng)烈建議您使用地圖符號(hào)!

 您必須了解地圖符號(hào)才能設(shè)置合同。請(qǐng)閱讀有關(guān)JSON Groovy文檔。

前面顯示的合同是雙方之間的協(xié)議,其中:

  • 如果HTTP請(qǐng)求與所有

    • /fraudcheck端點(diǎn)上的PUT方法,
    • 一個(gè)具有client.id且與正則表達(dá)式[0-9]{10}loanAmount等于99999匹配的JSON正文,
    • 和值為application/vnd.fraud.v1+jsonContent-Type標(biāo)頭,
  • 然后將HTTP響應(yīng)發(fā)送給使用者

    • 狀態(tài)為200,
    • 包含JSON正文,其fraudCheckStatus字段包含值FRAUD,而rejectionReason字段包含值Amount too high,
    • 還有一個(gè)Content-Type標(biāo)頭,其值為application/vnd.fraud.v1+json。

一旦準(zhǔn)備好在集成測(cè)試中實(shí)際檢查API,就需要在本地安裝存根。

添加Spring Cloud Contract驗(yàn)證程序插件。

我們可以添加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功能,這些功能來自提供的合同:

  • 生成并運(yùn)行測(cè)試
  • 制作并安裝存根

您不想生成測(cè)試,因?yàn)樽鳛橄M(fèi)者,您只想玩存根。您需要跳過測(cè)試的生成和執(zhí)行。執(zhí)行時(shí):

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

在日志中,您會(huì)看到以下內(nèi)容:

[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

它確認(rèn)http-server的存根已安裝在本地存儲(chǔ)庫中。

運(yùn)行集成測(cè)試。

為了從自動(dòng)存根下載的Spring Cloud Contract Stub Runner功能中受益,您必須在用戶端項(xiàng)目(Loan Application service)中執(zhí)行以下操作:

添加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>

將依賴項(xiàng)添加到Spring Cloud Contract Stub Runner

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

@AutoConfigureStubRunner注釋測(cè)試類。在注釋中,為Stub Runner提供group-idartifact-id,以下載合作者的存根。(可選步驟)由于您是與離線協(xié)作者一起玩,因此您還可以提供離線工作切換(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 {

現(xiàn)在,當(dāng)您運(yùn)行測(cè)試時(shí),您將看到類似以下的內(nèi)容:

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找到了您的存根,并為您的應(yīng)用啟動(dòng)了服務(wù)器,其組ID為com.example,工件ID為??http-server,存根的版本為0.0.1-SNAPSHOT,且分類器為stubs端口8080。

提出拉取請(qǐng)求。

到目前為止,您所做的是一個(gè)迭代過程。您可以試用合同,將其安裝在本地,然后在用戶端工作,直到合同按您的意愿運(yùn)行。

對(duì)結(jié)果滿意并通過測(cè)試后,將拉取請(qǐng)求發(fā)布到服務(wù)器端。目前,消費(fèi)者方面的工作已經(jīng)完成。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)