使用 Stub Runner Boot 應用程式

警告

由於當前artifact倉庫釋出工具的限制,我們目前無法釋出可執行的jar包,並且從4.1.6版本開始,我們跳過了此artifact的釋出。Stub Runner Boot仍然可以透過Docker Stub Runner Boot映象獲取,這是使用該應用程式的首選方式。您還可以訪問專案倉庫中的原始碼並自行構建應用程式。如果artifact倉庫工具進行了必要的調整,我們將恢復釋出此jar包。

Spring Cloud Contract Stub Runner Boot是一個Spring Boot應用程式,它暴露REST端點以觸發訊息標籤並訪問WireMock伺服器。

Stub Runner Boot安全性

Stub Runner Boot應用程式在設計上是不安全的——對其進行安全加固需要為所有存根新增安全性,即使它們實際上不需要。由於這是一個測試工具,該伺服器不應用於生產環境。

預計只有受信任的客戶端才能訪問Stub Runner Boot伺服器。您不應在不受信任的位置將此應用程式作為Fat Jar或Docker映象執行。

Stub Runner伺服器

要使用Stub Runner伺服器,請新增以下依賴

compile "org.springframework.cloud:spring-cloud-starter-stub-runner"

然後用@EnableStubRunnerServer註解一個類,構建一個fat jar,就可以使用了。

有關屬性,請參閱Stub Runner Spring部分。

Stub Runner Server Fat Jar

您可以透過執行以下命令從Maven下載一個獨立的JAR(例如,版本2.0.1.RELEASE)

$ wget -O stub-runner.jar 'https://search.maven.org/remotecontent?filepath=org/springframework/cloud/spring-cloud-contract-stub-runner-boot/2.0.1.RELEASE/spring-cloud-contract-stub-runner-boot-2.0.1.RELEASE.jar'
$ java -jar stub-runner.jar --spring.cloud.contract.stubrunner.ids=... --spring.cloud.contract.stubrunner.repositoryRoot=...

Spring Cloud CLI

Spring Cloud CLI專案的1.4.0.RELEASE版本開始,您可以透過執行spring cloud stubrunner來啟動Stub Runner Boot。

要傳遞配置,您可以在當前工作目錄、名為config的子目錄或~/.spring-cloud中建立一個spring.cloud.contract.stubrunner.yml檔案。該檔案可以類似於以下示例,用於執行本地安裝的存根

示例1. stubrunner.yml
spring.cloud.contract.stubrunner:
  stubsMode: LOCAL
  ids:
    - com.example:beer-api-producer:+:9876

然後,您可以在終端視窗中呼叫spring cloud stubrunner來啟動Stub Runner伺服器。它在埠8750可用。

端點

Stub Runner Boot提供兩個端點

HTTP

對於HTTP,Stub Runner Boot提供以下端點

  • GET /stubs:以ivy:integer表示法返回所有正在執行的存根列表

  • GET /stubs/{ivy}:返回給定ivy表示法的埠(呼叫端點時,ivy也可以僅是artifactId

訊息

對於訊息傳遞,Stub Runner Boot提供以下端點

  • GET /triggers:返回所有正在執行的標籤列表,採用ivy : [ label1, label2 …​]表示法

  • POST /triggers/{label}:執行帶有label的觸發器

  • POST /triggers/{ivy}/{label}:執行帶有給定ivy表示法的label的觸發器(呼叫端點時,ivy也可以僅是artifactId

示例

以下示例展示了Stub Runner Boot的典型用法

@SpringBootTest(classes = StubRunnerBoot, properties = "spring.cloud.zookeeper.enabled=false")
@ActiveProfiles("test")
class StubRunnerBootSpec {

	@Autowired
	StubRunning stubRunning

	@BeforeEach
	void setup() {
		RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning),
				new TriggerController(stubRunning))
	}

	@Test
	void 'should return a list of running stub servers in "full ivy port" notation'() {
		when:
			String response = RestAssuredMockMvc.get('/stubs').body.asString()
		then:
			def root = new JsonSlurper().parseText(response)
			assert root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs' instanceof Integer
	}

	@Test
	void 'should return a port on which a #stubId stub is running'() {
		given:
		def stubIds = ['org.springframework.cloud.contract.verifier.stubs:bootService:+:stubs',
				   'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs',
				   'org.springframework.cloud.contract.verifier.stubs:bootService:+',
				   'org.springframework.cloud.contract.verifier.stubs:bootService',
				   'bootService']
		stubIds.each {
			when:
				def response = RestAssuredMockMvc.get("/stubs/${it}")
			then:
				assert response.statusCode == 200
				assert Integer.valueOf(response.body.asString()) > 0
		}
	}

	@Test
	void 'should return 404 when missing stub was called'() {
		when:
			def response = RestAssuredMockMvc.get("/stubs/a:b:c:d")
		then:
			assert response.statusCode == 404
	}

	@Test
	void 'should return a list of messaging labels that can be triggered when version and classifier are passed'() {
		when:
			String response = RestAssuredMockMvc.get('/triggers').body.asString()
		then:
			def root = new JsonSlurper().parseText(response)
			assert root.'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'?.containsAll(["return_book_1"])
	}

	@Test
	void 'should trigger a messaging label'() {
		given:
			StubRunning stubRunning = Mockito.mock(StubRunning)
			RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning), new TriggerController(stubRunning))
		when:
			def response = RestAssuredMockMvc.post("/triggers/delete_book")
		then:
			response.statusCode == 200
		and:
			Mockito.verify(stubRunning).trigger('delete_book')
	}

	@Test
	void 'should trigger a messaging label for a stub with #stubId ivy notation'() {
		given:
			StubRunning stubRunning = Mockito.mock(StubRunning)
			RestAssuredMockMvc.standaloneSetup(new HttpStubsController(stubRunning), new TriggerController(stubRunning))
		and:
			def stubIds = ['org.springframework.cloud.contract.verifier.stubs:bootService:stubs', 'org.springframework.cloud.contract.verifier.stubs:bootService', 'bootService']
		stubIds.each {
			when:
				def response = RestAssuredMockMvc.post("/triggers/$it/delete_book")
			then:
				assert response.statusCode == 200
			and:
				Mockito.verify(stubRunning).trigger(it, 'delete_book')
		}

	}

	@Test
	void 'should throw exception when trigger is missing'() {
		when:
		BDDAssertions.thenThrownBy(() -> RestAssuredMockMvc.post("/triggers/missing_label"))
		.hasMessageContaining("Exception occurred while trying to return [missing_label] label.")
		.hasMessageContaining("Available labels are")
		.hasMessageContaining("org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs=[]")
		.hasMessageContaining("org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs=")
	}

}

Stub Runner Boot與服務發現

使用Stub Runner Boot的一種方式是將其用作“冒煙測試”的存根源。這意味著什麼?假設您不想將50個微服務部署到測試環境以檢視您的應用程式是否工作。您已經在構建過程中運行了一套測試,但您還想確保您的應用程式打包工作正常。您可以將您的應用程式部署到環境中,啟動它,並對其執行幾個測試以檢視它是否工作。我們可以將這些測試稱為“冒煙測試”,因為它們的目的只是檢查少數測試場景。

這種方法的問題是,如果您使用微服務,您很可能也使用服務發現工具。Stub Runner Boot允許您透過啟動所需的存根並將它們註冊到服務發現工具中來解決這個問題。

現在假設我們想要啟動這個應用程式,以便存根能夠自動註冊。我們可以透過執行java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar來做到這一點,其中${SYSTEM_PROPS}

這樣,您部署的應用程式就可以透過服務發現向已啟動的WireMock伺服器傳送請求。最有可能的是,第1到3點可以在application.yml中預設設定,因為它們不太可能更改。這樣,您只需在每次啟動Stub Runner Boot時提供要下載的存根列表即可。

© . This site is unofficial and not affiliated with VMware.