使用可插拔架構

您可能會遇到您的契約以其他格式定義的情況,例如 YAML、RAML 或 PACT。在這些情況下,您仍然希望受益於自動生成測試和存根。您可以新增自己的實現來生成測試和存根。此外,您可以自定義測試的生成方式(例如,您可以為其他語言生成測試)和存根的生成方式(例如,您可以為其他 HTTP 伺服器實現生成存根)。

自定義契約轉換器

ContractConverter 介面允許您註冊自己的契約結構轉換器實現。以下程式碼清單顯示了 ContractConverter 介面

import java.io.File;
import java.util.Collection;

/**
 * Converter to be used to convert FROM {@link File} TO {@link Contract} and from
 * {@link Contract} to {@code T}.
 *
 * @param <T> - type to which we want to convert the contract
 * @author Marcin Grzejszczak
 * @since 1.1.0
 */
public interface ContractConverter<T> extends ContractStorer<T>, ContractReader<T> {

	/**
	 * Should this file be accepted by the converter. Can use the file extension to check
	 * if the conversion is possible.
	 * @param file - file to be considered for conversion
	 * @return - {@code true} if the given implementation can convert the file
	 */
	boolean isAccepted(File file);

	/**
	 * Converts the given {@link File} to its {@link Contract} representation.
	 * @param file - file to convert
	 * @return - {@link Contract} representation of the file
	 */
	Collection<Contract> convertFrom(File file);

	/**
	 * Converts the given {@link Contract} to a {@link T} representation.
	 * @param contract - the parsed contract
	 * @return - {@link T} the type to which we do the conversion
	 */
	T convertTo(Collection<Contract> contract);

}

您的實現必須定義其應開始轉換的條件。此外,您必須定義如何雙向執行該轉換。

建立實現後,您必須建立一個 /META-INF/spring.factories 檔案,其中提供實現的完全限定名稱。

以下示例顯示了一個典型的 spring.factories 檔案

org.springframework.cloud.contract.spec.ContractConverter=\
org.springframework.cloud.contract.verifier.converter.YamlContractConverter

使用自定義測試生成器

如果您想為 Java 以外的語言生成測試,或者對驗證器構建 Java 測試的方式不滿意,您可以註冊自己的實現。

SingleTestGenerator 介面允許您註冊自己的實現。以下程式碼清單顯示了 SingleTestGenerator 介面

import java.nio.file.Path;
import java.util.Collection;

import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties;
import org.springframework.cloud.contract.verifier.file.ContractMetadata;

/**
 * Builds a single test.
 *
 * @since 1.1.0
 */
public interface SingleTestGenerator {

	/**
	 * Creates contents of a single test class in which all test scenarios from the
	 * contract metadata should be placed.
	 * @param properties - properties passed to the plugin
	 * @param listOfFiles - list of parsed contracts with additional metadata
	 * @param generatedClassData - information about the generated class
	 * @param includedDirectoryRelativePath - relative path to the included directory
	 * @return contents of a single test class
	 */
	String buildClass(ContractVerifierConfigProperties properties, Collection<ContractMetadata> listOfFiles,
			String includedDirectoryRelativePath, GeneratedClassData generatedClassData);

	class GeneratedClassData {

		public final String className;

		public final String classPackage;

		public final Path testClassPath;

		public GeneratedClassData(String className, String classPackage, Path testClassPath) {
			this.className = className;
			this.classPackage = classPackage;
			this.testClassPath = testClassPath;
		}

	}

}

同樣,您必須提供一個 spring.factories 檔案,例如以下示例中所示的檔案

org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
com.example.MyGenerator

使用自定義存根生成器

如果您想為 WireMock 以外的存根伺服器生成存根,您可以插入自己的 StubGenerator 介面實現。以下程式碼清單顯示了 StubGenerator 介面

import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.verifier.file.ContractMetadata;

/**
 * Converts contracts into their stub representation.
 *
 * @param <T> - type of stub mapping
 * @since 1.1.0
 */
public interface StubGenerator<T> {

	/**
	 * @param mapping - potential stub mapping mapping
	 * @return {@code true} if this converter could have generated this mapping stub.
	 */
	default boolean canReadStubMapping(File mapping) {
		return mapping.getName().endsWith(fileExtension());
	}

	/**
	 * @param rootName - root name of the contract
	 * @param content - metadata of the contract
	 * @return the collection of converted contracts into stubs. One contract can result
	 * in multiple stubs.
	 */
	Map<Contract, String> convertContents(String rootName, ContractMetadata content);

	/**
	 * Post process a generated stub mapping.
	 * @param stubMapping - mapping of a stub
	 * @param contract - contract for which stub was generated
	 * @return the converted stub mapping
	 */
	default T postProcessStubMapping(T stubMapping, Contract contract) {
		List<StubPostProcessor> processors = StubPostProcessor.PROCESSORS.stream()
			.filter(p -> p.isApplicable(contract))
			.collect(Collectors.toList());
		if (processors.isEmpty()) {
			return defaultStubMappingPostProcessing(stubMapping, contract);
		}
		T stub = stubMapping;
		for (StubPostProcessor processor : processors) {
			stub = (T) processor.postProcess(stub, contract);
		}
		return stub;
	}

	/**
	 * Stub mapping to chose when no post processors where found on the classpath.
	 * @param stubMapping - mapping of a stub
	 * @param contract - contract for which stub was generated
	 * @return the converted stub mapping
	 */
	default T defaultStubMappingPostProcessing(T stubMapping, Contract contract) {
		return stubMapping;
	}

	/**
	 * @param inputFileName - name of the input file
	 * @return the name of the converted stub file. If you have multiple contracts in a
	 * single file then a prefix will be added to the generated file. If you provide the
	 * {@link Contract#getName} field then that field will override the generated file
	 * name.
	 *
	 * Example: name of file with 2 contracts is {@code foo.groovy}, it will be converted
	 * by the implementation to {@code foo.json}. The recursive file converter will create
	 * two files {@code 0_foo.json} and {@code 1_foo.json}
	 */
	String generateOutputFileNameForInput(String inputFileName);

	/**
	 * Describes the file extension of the generated mapping that this stub generator can
	 * handle.
	 * @return string describing the file extension
	 */
	default String fileExtension() {
		return ".json";
	}

}

同樣,您必須提供一個 spring.factories 檔案,例如以下示例中所示的檔案

# Stub converters
org.springframework.cloud.contract.verifier.converter.StubGenerator=\
org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter

預設實現是 WireMock 存根生成。

您可以提供多個存根生成器實現。例如,從單個 DSL 中,您可以生成 WireMock 存根和 Pact 檔案。

使用自定義存根執行器

如果您決定使用自定義存根生成,您還需要一種自定義方式來執行不同存根提供程式的存根。

假設您使用 Moco 構建存根,並且您編寫了一個存根生成器並將存根放在 JAR 檔案中。

為了讓 Stub Runner 知道如何執行您的存根,您必須定義一個自定義 HTTP 存根伺服器實現,它可能類似於以下示例

import com.github.dreamhead.moco.bootstrap.arg.HttpArgs
import com.github.dreamhead.moco.runner.JsonRunner
import com.github.dreamhead.moco.runner.RunnerSetting
import groovy.transform.CompileStatic
import groovy.util.logging.Commons

import org.springframework.cloud.contract.stubrunner.HttpServerStub
import org.springframework.cloud.contract.stubrunner.HttpServerStubConfiguration

@Commons
@CompileStatic
class MocoHttpServerStub implements HttpServerStub {

	private boolean started
	private JsonRunner runner
	private int port

	@Override
	int port() {
		if (!isRunning()) {
			return -1
		}
		return port
	}

	@Override
	boolean isRunning() {
		return started
	}

	@Override
	HttpServerStub start(HttpServerStubConfiguration configuration) {
		this.port = configuration.port
		return this
	}

	@Override
	HttpServerStub stop() {
		if (!isRunning()) {
			return this
		}
		this.runner.stop()
		return this
	}

	@Override
	HttpServerStub registerMappings(Collection<File> stubFiles) {
		List<RunnerSetting> settings = stubFiles.findAll { it.name.endsWith("json") }
			.collect {
			log.info("Trying to parse [${it.name}]")
			try {
				return RunnerSetting.aRunnerSetting().addStream(it.newInputStream()).
					build()
			}
			catch (Exception e) {
				log.warn("Exception occurred while trying to parse file [${it.name}]", e)
				return null
			}
		}.findAll { it }
		this.runner = JsonRunner.newJsonRunnerWithSetting(settings,
			HttpArgs.httpArgs().withPort(this.port).build())
		this.runner.run()
		this.started = true
		return this
	}

	@Override
	String registeredMappings() {
		return ""
	}

	@Override
	boolean isAccepted(File file) {
		return file.name.endsWith(".json")
	}
}

然後,您可以在 spring.factories 檔案中註冊它,如以下示例所示

org.springframework.cloud.contract.stubrunner.HttpServerStub=\
org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub

現在您可以使用 Moco 執行存根了。

如果您不提供任何實現,則使用預設(WireMock)實現。如果您提供多個,則使用列表中的第一個。

使用自定義存根下載器

您可以透過建立 StubDownloaderBuilder 介面的實現來自定義存根的下載方式,如以下示例所示

class CustomStubDownloaderBuilder implements StubDownloaderBuilder {

	@Override
	public StubDownloader build(final StubRunnerOptions stubRunnerOptions) {
		return new StubDownloader() {
			@Override
			public Map.Entry<StubConfiguration, File> downloadAndUnpackStubJar(
					StubConfiguration config) {
				File unpackedStubs = retrieveStubs();
				return new AbstractMap.SimpleEntry<>(
						new StubConfiguration(config.getGroupId(), config.getArtifactId(), version,
								config.getClassifier()), unpackedStubs);
			}

			File retrieveStubs() {
			    // here goes your custom logic to provide a folder where all the stubs reside
			}
		}
	}
}

然後,您可以在 spring.factories 檔案中註冊它,如以下示例所示

# Example of a custom Stub Downloader Provider
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
com.example.CustomStubDownloaderBuilder

現在您可以選擇包含存根源的資料夾。

如果您不提供任何實現,則使用預設(掃描類路徑)實現。如果您提供 stubsMode = StubRunnerProperties.StubsMode.LOCALstubsMode = StubRunnerProperties.StubsMode.REMOTE,則使用 Aether 實現。如果您提供多個,則使用列表中的第一個。

使用 SCM 存根下載器

只要 repositoryRoot 以 SCM 協議開頭(目前,我們只支援 git://),存根下載器就會嘗試克隆儲存庫並將其用作生成測試或存根的契約源。

透過環境變數、系統屬性或外掛或契約儲存庫配置中設定的屬性,您可以調整下載器的行為。下表描述了可用的屬性

表 1. SCM 存根下載器屬性

屬性型別

屬性名稱

描述

* git.branch(外掛屬性)

* spring.cloud.contract.stubrunner.properties.git.branch(系統屬性)

* SPRING_CLOUD_CONTRACT_STUBRUNNER_PROPERTIES_GIT_BRANCH(環境變數)

master

要檢出的分支

* git.username(外掛屬性)

* spring.cloud.contract.stubrunner.properties.git.username(系統屬性)

* SPRING_CLOUD_CONTRACT_STUBRUNNER_PROPERTIES_GIT_USERNAME(環境變數)

Git 克隆使用者名稱

* git.password(外掛屬性)

* spring.cloud.contract.stubrunner.properties.git.password(系統屬性)

* SPRING_CLOUD_CONTRACT_STUBRUNNER_PROPERTIES_GIT_PASSWORD(環境變數)

Git 克隆密碼

* git.no-of-attempts(外掛屬性)

* spring.cloud.contract.stubrunner.properties.git.no-of-attempts(系統屬性)

* SPRING_CLOUD_CONTRACT_STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS(環境變數)

10

嘗試將提交推送到 origin 的次數

* git.wait-between-attempts(外掛屬性)

* spring.cloud.contract.stubrunner.properties.git.wait-between-attempts(系統屬性)

* SPRING_CLOUD_CONTRACT_STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS(環境變數)

1000

嘗試將提交推送到 origin 之間等待的毫秒數

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