下面列出了怎么用org.junit.jupiter.api.extension.ExtensionConfigurationException的API类实例代码及写法,或者点击链接到github查看源代码。
private static Future<String> resolveImage(Optional<Path> dockerfile) {
if (isHollow) {
// Testcontainers won't be used in this case, supply a dummy image to improve performance
return CompletableFuture.completedFuture("alpine:3.5");
} else if (dockerfile.isPresent()) {
if (!Files.exists(dockerfile.get()))
throw new ExtensionConfigurationException("Dockerfile did not exist at: " + dockerfile.get());
ImageFromDockerfile image = new ImageFromDockerfile("testcontainers/mpapp-" + Base58.randomString(10).toLowerCase());
image.withDockerfile(dockerfile.get());
image.setBaseDirectory(Paths.get("."));
return image;
} else {
// Dockerfile is not present, use a ServerAdapter to build the image
return resolveAdatper().orElseThrow(() -> {
return new ExtensionConfigurationException("Unable to resolve Docker image for application because:" +
"\n - unable to locate Dockerfile in " + dockerfile_root.toAbsolutePath() +
"\n - unable to locate Dockerfile in " + dockerfile_src_main.toAbsolutePath() +
"\n - did not find any ServerAdapter to provide a default Dockerfile");
}).getDefaultImage(findAppFile());
}
}
private Set<GenericContainer<?>> discoverContainers(Class<?> clazz) {
Set<GenericContainer<?>> discoveredContainers = new HashSet<>();
for (Field containerField : AnnotationSupport.findAnnotatedFields(clazz, Container.class)) {
if (!Modifier.isPublic(containerField.getModifiers()))
throw new ExtensionConfigurationException("@Container annotated fields must be public visibility");
if (!Modifier.isStatic(containerField.getModifiers()))
throw new ExtensionConfigurationException("@Container annotated fields must be static");
boolean isStartable = GenericContainer.class.isAssignableFrom(containerField.getType());
if (!isStartable)
throw new ExtensionConfigurationException("@Container annotated fields must be a subclass of " + GenericContainer.class);
try {
GenericContainer<?> startableContainer = (GenericContainer<?>) containerField.get(null);
discoveredContainers.add(startableContainer);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOG.warn("Unable to access field " + containerField, e);
}
}
return discoveredContainers;
}
@Override
public void applyConfiguration(Class<?> testClass) {
containers = discoveredContainers.computeIfAbsent(testClass, clazz -> new ContainerGroup(clazz));
// Verify that @MicroShedTest comes before @QuarkusTest
if (containers.allContainers.size() > 0) {
boolean foundQuarkusTest = false;
for (Annotation anno : testClass.getAnnotations()) {
if (anno.annotationType().getCanonicalName().equals("io.quarkus.test.junit.QuarkusTest"))
foundQuarkusTest = true;
else if (foundQuarkusTest && anno.annotationType().equals(MicroShedTest.class))
throw new ExtensionConfigurationException("Must speciy @MicroShedTest annotation before @QuarkusTest so external " +
"services can be started and discovered before Quarkus starts.");
}
}
String appUrl = getApplicationURL();
LOG.info("Using Quarkus application URL: " + appUrl);
ManuallyStartedConfiguration.setRuntimeURL(appUrl);
super.applyConfiguration(testClass);
}
@Test
@DisplayName("should raise ExtensionConfigurationException when multiple configs are present.")
void shouldRaiseExceptionWhenMultipleConfigsArePresent() {
// given
final Class<?> testClass = InvalidMultipleOptionsTestCase.class;
// when
final TestResults results = launchTests(testClass);
// then
then(results.getSummary().getTestsFailedCount())
.describedAs("test instance preparation expected to fail")
.isEqualTo(1L);
then(results.getThrowables()).hasSize(1);
then(results.getThrowables().get(0))
.isInstanceOf(ExtensionConfigurationException.class)
.hasMessageContaining("@ConfigureWireMock");
}
private Set<GenericContainer<?>> discoverContainers(Class<?> clazz) {
Set<GenericContainer<?>> discoveredContainers = new HashSet<>();
for (Field containerField : AnnotationSupport.findAnnotatedFields(clazz, Container.class)) {
if (!Modifier.isPublic(containerField.getModifiers()))
throw new ExtensionConfigurationException("@Container annotated fields must be public visibility");
if (!Modifier.isStatic(containerField.getModifiers()))
throw new ExtensionConfigurationException("@Container annotated fields must be static");
boolean isStartable = GenericContainer.class.isAssignableFrom(containerField.getType());
if (!isStartable)
throw new ExtensionConfigurationException("@Container annotated fields must be a subclass of " + GenericContainer.class);
try {
GenericContainer<?> startableContainer = (GenericContainer<?>) containerField.get(null);
discoveredContainers.add(startableContainer);
} catch (IllegalArgumentException | IllegalAccessException e) {
LOG.warn("Unable to access field " + containerField, e);
}
}
return discoveredContainers;
}
private static void injectRestClients(Class<?> clazz, TestcontainersConfiguration config) throws Exception {
List<Field> restClientFields = AnnotationSupport.findAnnotatedFields(clazz, Inject.class);
if (restClientFields.size() == 0)
return;
String mpAppURL = config.getApplicationURL();
for (Field restClientField : restClientFields) {
if (!Modifier.isPublic(restClientField.getModifiers()) ||
!Modifier.isStatic(restClientField.getModifiers()) ||
Modifier.isFinal(restClientField.getModifiers())) {
throw new ExtensionConfigurationException("REST-client field must be public, static, and non-final: " + restClientField.getName());
}
String jwt = createJwtIfNeeded(restClientField);
Object restClient = JAXRSUtilities.createRestClient(restClientField.getType(), mpAppURL, jwt);
//Object restClient = JAXRSUtilities.createRestClient(restClientField.getType(), mpAppURL);
restClientField.set(null, restClient);
LOGGER.debug("Injecting rest client for " + restClientField);
}
}
private static InjectWith getInjectWith(Class<?> cls) {
InjectWith result = null;
Class<?> firstClassMatched = null;
while (cls != null) {
InjectWith iw = cls.getAnnotation(InjectWith.class);
if (iw != null) {
if(result != null) {
throw new ExtensionConfigurationException(String.format("Multiple @InjectWith annotations are found (%s and %s). Only a single one is supported.",
firstClassMatched.getName(), cls.getName()));
}
result = iw;
firstClassMatched = cls;
}
cls = cls.getEnclosingClass();
}
return result;
}
@Override
public void beforeAll(ExtensionContext context) {
Class<?> testClass = context.getTestClass()
.orElseThrow(() -> new ExtensionConfigurationException("TestcontainersExtension is only supported for classes."));
Store store = context.getStore(NAMESPACE);
List<StoreAdapter> sharedContainersStoreAdapters = findSharedContainers(testClass);
sharedContainersStoreAdapters.forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start()));
List<TestLifecycleAware> lifecycleAwareContainers = sharedContainersStoreAdapters
.stream()
.filter(this::isTestLifecycleAware)
.map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container)
.collect(toList());
store.put(SHARED_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers);
signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context));
}
public ContainerGroup(Class<?> testClass) {
this.testClass = testClass;
sharedConfigClass = testClass.isAnnotationPresent(SharedContainerConfig.class) ? //
testClass.getAnnotation(SharedContainerConfig.class).value() : null;
unsharedContainers = Collections.unmodifiableSet(discoverContainers(testClass));
sharedContainers = hasSharedConfig() ? //
Collections.unmodifiableSet(discoverContainers(sharedConfigClass)) : //
Collections.emptySet();
Set<GenericContainer<?>> all = new HashSet<>(unsharedContainers);
all.addAll(sharedContainers);
allContainers = Collections.unmodifiableSet(all);
Set<ApplicationContainer> apps = allContainers.stream()
.filter(c -> c instanceof ApplicationContainer)
.map(c -> (ApplicationContainer) c)
.collect(Collectors.toSet());
if (apps.size() == 0) {
app = null;
} else if (apps.size() == 1) {
app = apps.iterator().next();
} else {
app = null;
// Error: Multiple ApplicationContainers were found
String appString = apps.stream()
.map(app -> app.toStringSimple())
.collect(Collectors.joining(", "));
throw new ExtensionConfigurationException("Only 1 ApplicationContainer may be used, but multiple were defined: " +
appString);
}
}
@Override
public String getApplicationURL() {
ApplicationContainer mpApp = containers.app;
if (mpApp == null) {
String sharedConfigMsg = containers.hasSharedConfig() ? " or " + containers.sharedConfigClass : "";
throw new ExtensionConfigurationException("No public static ApplicationContainer fields annotated with @Container were located " +
"on " + containers.testClass + sharedConfigMsg + ".");
}
return mpApp.getApplicationURL();
}
private void configureKafka(Set<GenericContainer<?>> containers) {
Class<?> KafkaContainer = tryLoad("org.testcontainers.containers.KafkaContainer");
if (KafkaContainer == null)
return;
Set<GenericContainer<?>> kafkaContainers = containers.stream()
.filter(c -> KafkaContainer.isAssignableFrom(c.getClass()))
.collect(Collectors.toSet());
if (kafkaContainers.size() != 1)
return;
// At this point we have found exactly 1 kafka container
GenericContainer<?> kafka = kafkaContainers.iterator().next();
// Configure app container with bootstrap server
String bootstrapProperty = "MP_MESSAGING_CONNECTOR_LIBERTY_KAFKA_BOOTSTRAP_SERVERS";
String bootstrapServer = null;
if (ApplicationEnvironment.Resolver.isSelected(TestcontainersConfiguration.class)) {
if (kafka.getNetworkAliases().size() == 0)
throw new ExtensionConfigurationException("Unable to configure kafka bootstrap server because no network alias is defined");
bootstrapServer = kafka.getNetworkAliases().get(kafka.getNetworkAliases().size() - 1) + ":9092";
} else if (ApplicationEnvironment.Resolver.isSelected(HollowTestcontainersConfiguration.class)) {
bootstrapServer = "localhost:9093";
} else {
return;
}
String finalBootstrapServers = bootstrapServer;
LOG.info("Auto-configuring ApplicationContainer instances with " + bootstrapProperty + "=" + finalBootstrapServers);
containers.stream()
.filter(c -> ApplicationContainer.class.isAssignableFrom(c.getClass()))
.filter(c -> c.getNetwork().equals(kafka.getNetwork()))
.filter(c -> !c.getEnvMap().containsKey(bootstrapProperty))
.filter(c -> !c.getEnvMap().containsKey(bootstrapProperty.toLowerCase()))
.forEach(c -> c.withEnv(bootstrapProperty, finalBootstrapServers));
}
Properties getProducerProperties(Field producerField) {
KafkaProducerClient producerConfig = producerField.getAnnotation(KafkaProducerClient.class);
Properties properties = new Properties();
String bootstrapServers = producerConfig.bootstrapServers().isEmpty() ? globalBootstrapServers : producerConfig.bootstrapServers();
if (bootstrapServers.isEmpty())
throw new ExtensionConfigurationException("To use @KafkaProducerClient on a KafkaProducer a bootstrap server must be " +
"defined in the @KafkaProducerClient annotation or using the " +
"'org.microshed.kafka.bootstrap.servers' system property");
properties.put("bootstrap.servers", bootstrapServers);
if (isClassPropertySet(producerConfig.keySerializer().getName()))
properties.put("key.serializer", producerConfig.keySerializer().getName());
if (isClassPropertySet(producerConfig.valueSerializer().getName()))
properties.put("value.serializer", producerConfig.valueSerializer().getName());
for (String prop : producerConfig.properties()) {
int split = prop.indexOf("=");
if (split < 2)
throw new ExtensionConfigurationException("The property '" + prop + "' for field " + producerField + " must be in the format 'key=value'");
properties.put(prop.substring(0, split), prop.substring(split + 1));
}
// Auto-detect key/value serializers if needed
if (producerField.getGenericType() instanceof ParameterizedType) {
if (!properties.containsKey("key.serializer")) {
Type keyType = ((ParameterizedType) producerField.getGenericType()).getActualTypeArguments()[0];
if (defaultSerailizers.containsKey(keyType))
properties.put("key.serializer", defaultSerailizers.get(keyType));
}
if (!properties.containsKey("value.serializer")) {
Type valueType = ((ParameterizedType) producerField.getGenericType()).getActualTypeArguments()[1];
if (defaultSerailizers.containsKey(valueType))
properties.put("value.serializer", defaultSerailizers.get(valueType));
}
}
return properties;
}
Properties getConsumerProperties(Field consumerField) {
KafkaConsumerClient consumerConfig = consumerField.getAnnotation(KafkaConsumerClient.class);
Properties properties = new Properties();
String bootstrapServers = consumerConfig.bootstrapServers().isEmpty() ? globalBootstrapServers : consumerConfig.bootstrapServers();
if (bootstrapServers.isEmpty())
throw new ExtensionConfigurationException("To use @KafkaConsumerClient on a KafkaConsumer a bootstrap server must be " +
"defined in the @KafkaConsumerClient annotation or using the " +
"'org.microshed.kafka.bootstrap.servers' system property");
properties.put("bootstrap.servers", bootstrapServers);
properties.put("group.id", consumerConfig.groupId());
if (isClassPropertySet(consumerConfig.keyDeserializer().getName()))
properties.put("key.deserializer", consumerConfig.keyDeserializer().getName());
if (isClassPropertySet(consumerConfig.valueDeserializer().getName()))
properties.put("value.deserializer", consumerConfig.valueDeserializer().getName());
for (String prop : consumerConfig.properties()) {
int split = prop.indexOf("=");
if (split < 2)
throw new ExtensionConfigurationException("The property '" + prop + "' for field " + consumerField + " must be in the format 'key=value'");
properties.put(prop.substring(0, split), prop.substring(split + 1));
}
// Auto-detect key/value deserializer if needed
if (consumerField.getGenericType() instanceof ParameterizedType) {
if (!properties.containsKey("key.deserializer")) {
Type keyType = ((ParameterizedType) consumerField.getGenericType()).getActualTypeArguments()[0];
if (defaultDeserailizers.containsKey(keyType))
properties.put("key.deserializer", defaultDeserailizers.get(keyType));
}
if (!properties.containsKey("value.deserializer")) {
Type valueType = ((ParameterizedType) consumerField.getGenericType()).getActualTypeArguments()[1];
if (defaultDeserailizers.containsKey(valueType))
properties.put("value.deserializer", defaultDeserailizers.get(valueType));
}
}
return properties;
}
public String getApplicationURL() {
MicroProfileApplication<?> mpApp = autoDiscoverMPApp(testClass, true);
// At this point we have found exactly one MicroProfileApplication
if (!mpApp.isCreated() || !mpApp.isRunning())
throw new ExtensionConfigurationException("MicroProfileApplication " + mpApp.getDockerImageName() + " is not running yet. " +
"The contianer must be running in order to obtain its URL.");
return mpApp.getApplicationURL();
}
private MicroProfileApplication<?> autoDiscoverMPApp(Class<?> clazz, boolean errorIfNone) {
// First check for any MicroProfileApplicaiton directly present on the test class
List<Field> mpApps = AnnotationSupport.findAnnotatedFields(clazz, Container.class,
f -> Modifier.isStatic(f.getModifiers()) &&
Modifier.isPublic(f.getModifiers()) &&
MicroProfileApplication.class.isAssignableFrom(f.getType()),
HierarchyTraversalMode.TOP_DOWN);
if (mpApps.size() == 1)
try {
return (MicroProfileApplication<?>) mpApps.get(0).get(null);
} catch (IllegalArgumentException | IllegalAccessException e) {
// This should never happen because we only look for fields that are public+static
e.printStackTrace();
}
if (mpApps.size() > 1)
throw new ExtensionConfigurationException("Should be no more than 1 public static MicroProfileApplication field on " + clazz);
// If none found, check any SharedContainerConfig
String sharedConfigMsg = "";
if (sharedConfigClass != null) {
MicroProfileApplication<?> mpApp = autoDiscoverMPApp(sharedConfigClass, false);
if (mpApp != null)
return mpApp;
sharedConfigMsg = " or " + sharedConfigClass;
}
if (errorIfNone)
throw new ExtensionConfigurationException("No public static MicroProfileApplication fields annotated with @Container were located " +
"on " + clazz + sharedConfigMsg + " to auto-connect with REST-client fields.");
return null;
}
@Override
public void beforeEach(ExtensionContext context) throws Exception {
IInjectorProvider injectorProvider = getOrCreateInjectorProvider(context);
if (injectorProvider instanceof IRegistryConfigurator) {
final IRegistryConfigurator registryConfigurator = (IRegistryConfigurator) injectorProvider;
registryConfigurator.setupRegistry();
}
if (injectorProvider != null) {
Injector injector = injectorProvider.getInjector();
if (injector != null) {
Object testInstance = context.getRequiredTestInstance();
injector.injectMembers(testInstance);
try {
TestInstances requiredTestInstances = context.getRequiredTestInstances();
for (Object o : requiredTestInstances.getEnclosingInstances()) {
injector.injectMembers(o);
}
} catch (NoSuchMethodError e) {
if (!Modifier.isStatic(testInstance.getClass().getModifiers())) {
if (testInstance.getClass().getDeclaringClass() != null) {
throw new ExtensionConfigurationException("Injection of nested classes needs Junit5 >= 5.4", e);
}
}
// OK, getRequiredTestInstances is not there in Junit5 < 5.4
}
}
}
}
private static Predicate<Field> isContainer() {
return field -> {
boolean isAnnotatedWithContainer = AnnotationSupport.isAnnotated(field, Container.class);
if (isAnnotatedWithContainer) {
boolean isStartable = Startable.class.isAssignableFrom(field.getType());
if (!isStartable) {
throw new ExtensionConfigurationException(String.format("FieldName: %s does not implement Startable", field.getName()));
}
return true;
}
return false;
};
}
private static StoreAdapter getContainerInstance(final Object testInstance, final Field field) {
try {
field.setAccessible(true);
Startable containerInstance = Preconditions.notNull((Startable) field.get(testInstance), "Container " + field.getName() + " needs to be initialized");
return new StoreAdapter(field.getDeclaringClass(), field.getName(), containerInstance);
} catch (IllegalAccessException e) {
throw new ExtensionConfigurationException("Can not access container defined in field " + field.getName());
}
}
@Override public Object resolveParameter(final ParameterContext parameterContext,
final ExtensionContext extensionContext) throws ParameterResolutionException {
Class<?> type = parameterContext.getParameter().getType();
if (Session.class.isAssignableFrom(type)) {
return getOrCreate(extensionContext).session;
} else if (Cluster.class.isAssignableFrom(type)) {
return getOrCreate(extensionContext).cluster;
}
throw new ExtensionConfigurationException(
String.format(Locale.ROOT, "%s supports only %s or %s but yours was %s",
CassandraExtension.class.getSimpleName(),
Session.class.getName(), Cluster.class.getName(), type.getName()));
}
@Override
public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(ExtensionContext context) {
Method testMethod = context.getRequiredTestMethod();
return AnnotationUtils.findAnnotation(testMethod, testAnnotationClass)
.map(annotation -> provideInvocationContexts(context, annotation))
.orElseThrow(() -> new ExtensionConfigurationException(String.format(
"Could not find annotation '%s' on test method '%s'.", testAnnotationClass, testMethod)));
}
@Test
void testProvideTestTemplateInvocationContextsShouldThrowProperExceptionIfAnnotationIsNotPresent() {
// Given:
when(extensionContext.getRequiredTestMethod()).thenReturn(testMethod);
// When:
Exception result = assertThrows(ExtensionConfigurationException.class,
() -> underTest.provideTestTemplateInvocationContexts(extensionContext));
// Then:
assertThat(result).hasMessageMatching("Could not find annotation '.*' on test method '.*'\\.");
}
@Override
public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException {
final Class<?> type = parameterContext.getParameter().getType();
if (MongoDatabase.class.isAssignableFrom(type)) {
return getOrCreate(extensionContext).instance.database();
} else if (MongoClient.class.isAssignableFrom(type)) {
return getOrCreate(extensionContext).instance.client();
}
throw new ExtensionConfigurationException(String.format("%s supports only %s or %s but yours was %s", MongoExtension.class.getSimpleName(),
MongoDatabase.class.getName(), MongoClient.class.getName(), type.getName()));
}
@Test
public void testTwoApps() {
assertThrows(ExtensionConfigurationException.class, () -> {
new TestcontainersConfiguration().applyConfiguration(TwoAppsClass.class);
});
}
@Override
public ConditionEvaluationResult evaluateExecutionCondition(ExtensionContext context) {
return findTestcontainers(context).map(this::evaluate)
.orElseThrow(() -> new ExtensionConfigurationException("@Testcontainers not found"));
}