スタブランナー Boot アプリケーションの使用

Spring Cloud Contract Stub Runner Boot は、メッセージングラベルをトリガーし、WireMock サーバーにアクセスするための REST エンドポイントを公開する Spring Boot アプリケーションです。

スタブランナー Boot セキュリティ

スタブランナー Boot アプリケーションは設計上セキュリティ保護されていません。セキュリティを確保するには、実際にはセキュリティが必要でない場合でも、すべてのスタブにセキュリティを追加する必要があります。これはテストユーティリティであるため、サーバーは運用環境で使用することを目的としていません

信頼されたクライアントのみがスタブランナー Boot サーバーにアクセスできることが期待されます。信頼できない場所では、このアプリケーションを Fat Jar または Docker イメージとして実行しないでください。

スタブランナーサーバー

スタブランナーサーバーを使用するには、次の依存関係を追加します。

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

次に、クラスに @EnableStubRunnerServer のアノテーションを付けて fat jar をビルドすると、動作する準備が整います。

プロパティについては、"スタブランナー Spring" セクションを参照してください。

スタブランナーサーバー Fat Jar

次のコマンドを実行すると、Maven (バージョン 2.0.1.RELEASE など) からスタンドアロン JAR をダウンロードできます。

$ 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 --stubrunner.ids=... --stubrunner.repositoryRoot=...

Spring Cloud CLI

Spring Cloud CLI (英語) プロジェクトの 1.4.0.RELEASE バージョンからは、spring cloud stubrunner を実行してスタブランナー Boot を開始できます。

構成を渡すには、現在の作業ディレクトリ、config というサブディレクトリ、または ~/.spring-cloud に stubrunner.yml ファイルを作成します。ファイルは、ローカルにインストールされたスタブを実行する場合の次の例のようになります。

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

その後、ターミナルウィンドウから spring cloud stubrunner を呼び出して、Stub Runner サーバーを起動できます。ポート 8750 で使用できます。

エンドポイント

Stub Runner Boot は 2 つのエンドポイントを提供します。

HTTP

HTTP の場合、スタブランナー Boot により次のエンドポイントが使用可能になります。

  • /stubs を入手: 実行中のすべてのスタブのリストを ivy:integer 表記で返します

  • /stubs/{ivy} を入手: 指定された ivy 表記のポートを返します (エンドポイントを呼び出す場合、ivy は artifactId のみにすることもできます)

メッセージング

メッセージングの場合、スタブランナー Boot により次のエンドポイントが利用可能になります。

  • /triggers を入手: 実行中のすべてのラベルのリストを ivy : [ label1, label2 …​] 表記で返します

  • ポスト /triggers/{label}label でトリガーを実行します

  • ポスト /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=")
	}

}

サービス検出によるスタブランナー Boot

Stub Runner Boot を使用する 1 つの方法は、「スモークテスト」用のスタブのフィードとして使用することです。それはどういう意味でしょうか? アプリケーションが動作するかどうかを確認するために、50 個のマイクロサービスをテスト環境にデプロイしたくないと仮定します。ビルドプロセス中に一連のテストをすでに実行していますが、アプリケーションのパッケージ化が機能することも確認したいと考えています。アプリケーションを環境にデプロイして起動し、いくつかのテストを実行して動作するかどうかを確認できます。これらのテストは、少数のテストシナリオのみをチェックすることが目的であるため、「スモークテスト」と呼ぶことができます。

このアプローチの課題は、マイクロサービスを使用する場合、サービス検出ツールも使用する可能性が高いことです。スタブランナー Boot を使用すると、必要なスタブを起動し、サービス検出ツールに登録することで、この課題を解決できます。

ここで、スタブが自動的に登録されるようにこのアプリケーションを起動するとします。これを行うには、アプリケーションを java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar${SYSTEM_PROPS}) で実行します。

これにより、デプロイされたアプリケーションは、サービス検出を通じて開始された WireMock サーバーにリクエストを送信できるようになります。おそらく、ポイント 1 ~ 3 は変更される可能性が低いため、application.yml でデフォルトで設定される可能性があります。こうすることで、スタブランナー Boot を開始するたびにダウンロードするスタブのリストのみを提供できます。