Skip to content

Commit

Permalink
Add ability to run tests with different config profiles
Browse files Browse the repository at this point in the history
  • Loading branch information
stuartwdouglas committed Jun 11, 2020
1 parent 885008e commit 0501a5d
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import java.util.function.Consumer;
import java.util.stream.Collectors;

import org.eclipse.microprofile.config.spi.ConfigBuilder;
import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.jboss.logging.Logger;

Expand Down Expand Up @@ -46,6 +47,7 @@
import io.quarkus.deployment.pkg.builditem.NativeImageBuildItem;
import io.quarkus.runtime.LaunchMode;
import io.quarkus.runtime.configuration.ProfileManager;
import io.smallrye.config.PropertiesConfigSource;

/**
* The augmentation task that produces the application.
Expand Down Expand Up @@ -219,6 +221,15 @@ private BuildResult runAugment(boolean firstRun, Set<String> changedResources, C
if (quarkusBootstrap.getBaseName() != null) {
builder.setBaseName(quarkusBootstrap.getBaseName());
}
if (!quarkusBootstrap.getConfigOverrides().isEmpty()) {
builder.setConfigCustomizer(new Consumer<ConfigBuilder>() {
@Override
public void accept(ConfigBuilder configBuilder) {
configBuilder.withSources(
new PropertiesConfigSource(quarkusBootstrap.getConfigOverrides(), "Config Overrides", 1000));
}
});
}

builder.setLaunchMode(launchMode);
builder.setRebuild(quarkusBootstrap.isRebuild());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Properties;

Expand Down Expand Up @@ -80,6 +81,7 @@ public class QuarkusBootstrap implements Serializable {
private final boolean disableClasspathCache;
private final AppModel existingModel;
private final boolean rebuild;
private final Map<String, String> configOverrides;

private QuarkusBootstrap(Builder builder) {
this.applicationRoot = builder.applicationRoot;
Expand Down Expand Up @@ -107,6 +109,7 @@ private QuarkusBootstrap(Builder builder) {
this.disableClasspathCache = builder.disableClasspathCache;
this.existingModel = builder.existingModel;
this.rebuild = builder.rebuild;
this.configOverrides = builder.configOverrides == null ? Collections.emptyMap() : builder.configOverrides;
}

public CuratedApplication bootstrap() throws BootstrapException {
Expand Down Expand Up @@ -163,6 +166,10 @@ public List<AdditionalDependency> getAdditionalApplicationArchives() {
return Collections.unmodifiableList(additionalApplicationArchives);
}

public Map<String, String> getConfigOverrides() {
return Collections.unmodifiableMap(configOverrides);
}

public List<Path> getAdditionalDeploymentArchives() {
return Collections.unmodifiableList(additionalDeploymentArchives);
}
Expand Down Expand Up @@ -238,6 +245,7 @@ public static class Builder {
List<AppDependency> forcedDependencies = new ArrayList<>();
boolean disableClasspathCache;
AppModel existingModel;
Map<String, String> configOverrides;

public Builder() {
}
Expand Down Expand Up @@ -270,6 +278,15 @@ public Builder addExcludedPath(Path path) {
return this;
}

public Map<String, String> getConfigOverrides() {
return configOverrides;
}

public Builder setConfigOverrides(Map<String, String> configOverrides) {
this.configOverrides = configOverrides;
return this;
}

/**
* The project root, used only for project dependency discovery.
*/
Expand Down
7 changes: 7 additions & 0 deletions integration-tests/main/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,13 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<runOrder>alphabetical</runOrder>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkus.it.rest;

import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.jboss.resteasy.annotations.jaxrs.PathParam;

@Path("/greeting")
public class GreetingEndpoint {

@Inject
GreetingService greetingService;

@GET
@Produces(MediaType.TEXT_PLAIN)
@Path("{name}")
public String greet(@PathParam String name) {
return greetingService.greet(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.quarkus.it.rest;

import javax.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class GreetingService {
public String greet(String greeting) {
return "Hello " + greeting;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.quarkus.it.main;

import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.inject.Alternative;

import io.quarkus.it.rest.GreetingService;

@ApplicationScoped
@Alternative
public class BonjourService extends GreetingService {

@Override
public String greet(String greeting) {
return "Bonjour " + greeting;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package io.quarkus.it.main;

import static org.hamcrest.Matchers.is;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
import io.restassured.RestAssured;

@QuarkusTest
public class GreetingNormalTestCase {

@Test
public void included() {
RestAssured.when()
.get("/greeting/Stu")
.then()
.statusCode(200)
.body(is("Hello Stu"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.quarkus.it.main;

import static org.hamcrest.Matchers.is;

import java.util.Collections;
import java.util.Set;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;
import io.quarkus.test.junit.QuarkusTestProfile;
import io.quarkus.test.junit.WithProfile;
import io.restassured.RestAssured;

/**
* Tests that QuarkusTestProfile works as expected
*/
@QuarkusTest
@WithProfile(GreetingProfileTestCase.MyProfile.class)
public class GreetingProfileTestCase {

@Test
public void included() {
RestAssured.when()
.get("/greeting/Stu")
.then()
.statusCode(200)
.body(is("Bonjour Stu"));
}

public static class MyProfile implements QuarkusTestProfile {

@Override
public Set<Class<?>> getEnabledAlternatives() {
return Collections.singleton(BonjourService.class);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import javax.enterprise.inject.Alternative;

import org.eclipse.microprofile.config.spi.ConfigProviderResolver;
import org.jboss.jandex.AnnotationInstance;
Expand Down Expand Up @@ -90,10 +94,12 @@ public class QuarkusTestExtension
private static List<Object> beforeEachCallbacks = new ArrayList<>();
private static List<Object> afterEachCallbacks = new ArrayList<>();
private static Class<?> quarkusTestMethodContextClass;
private static Class<? extends QuarkusTestProfile> quarkusTestProfile;

private static DeepClone deepClone;

private ExtensionState doJavaStart(ExtensionContext context) throws Throwable {
private ExtensionState doJavaStart(ExtensionContext context, Class<? extends QuarkusTestProfile> profile) throws Throwable {
quarkusTestProfile = profile;
Closeable testResourceManager = null;
try {
final LinkedBlockingDeque<Runnable> shutdownTasks = new LinkedBlockingDeque<>();
Expand All @@ -118,6 +124,21 @@ private ExtensionState doJavaStart(ExtensionContext context) throws Throwable {
final QuarkusBootstrap.Builder runnerBuilder = QuarkusBootstrap.builder()
.setIsolateDeployment(true)
.setMode(QuarkusBootstrap.Mode.TEST);
if (profile != null) {
QuarkusTestProfile profileInstance = profile.newInstance();
Map<String, String> additional = new HashMap<>(profileInstance.getConfigOverrides());
if (!profileInstance.getEnabledAlternatives().isEmpty()) {
additional.put("quarkus.arc.selected-alternatives", profileInstance.getEnabledAlternatives().stream()
.peek((c) -> {
if (!c.isAnnotationPresent(Alternative.class)) {
throw new RuntimeException(
"Enabled alternative " + c + " is not annotated with @Alternative");
}
})
.map(Class::getName).collect(Collectors.joining(",")));
}
runnerBuilder.setConfigOverrides(additional);
}

runnerBuilder.setProjectRoot(Paths.get("").normalize().toAbsolutePath());

Expand Down Expand Up @@ -310,10 +331,25 @@ private ExtensionState ensureStarted(ExtensionContext extensionContext) {
ExtensionContext root = extensionContext.getRoot();
ExtensionContext.Store store = root.getStore(ExtensionContext.Namespace.GLOBAL);
ExtensionState state = store.get(ExtensionState.class.getName(), ExtensionState.class);
if (state == null && !failedBoot) {
WithProfile annotation = extensionContext.getRequiredTestClass().getAnnotation(WithProfile.class);
Class<? extends QuarkusTestProfile> selectedProfile = null;
if (annotation != null) {
selectedProfile = annotation.value();
}
boolean wrongProfile = !Objects.equals(selectedProfile, quarkusTestProfile);
if ((state == null && !failedBoot) || wrongProfile) {
if (wrongProfile) {
if (state != null) {
try {
state.close();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
PropertyTestUtil.setLogFileProperty();
try {
state = doJavaStart(extensionContext);
state = doJavaStart(extensionContext, selectedProfile);
store.put(ExtensionState.class.getName(), state);

} catch (Throwable e) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package io.quarkus.test.junit;

import java.util.Collections;
import java.util.Map;
import java.util.Set;

/**
* Defines a 'test profile'. Tests run under a test profile
* will have different configuration options to other tests.
*
*/
public interface QuarkusTestProfile {

/**
* Returns additional config to be applied to the test. This
* will override any existing config (including in application.properties),
* however existing config will be merged with this (i.e. application.properties
* config will still take effect, unless a specific config key has been overridden).
*/
default Map<String, String> getConfigOverrides() {
return Collections.emptyMap();
}

/**
* Returns enabled alternatives.
*
* This has the same effect as setting the 'quarkus.arc.selected-alternatives' config key,
* however it may be more convenient.
*/
default Set<Class<?>> getEnabledAlternatives() {
return Collections.emptySet();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package io.quarkus.test.junit;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Defines a 'test profile'. Tests run under a test profile
* will have different configuration options to other tests.
*
* Due to the global nature of Quarkus if a previous test was
* run under a different profile then Quarkus will need to be
* restarted when the profile changes. Unfortunately there
* is currently no way to order tests based on profile, however
* this can be done manually by running tests in alphabetical
* order and putting all tests with the same profile in the same
* package.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface WithProfile {

/**
* The test profile to use. If subsequent tests use the same
* profile then Quarkus will not be restarted between tests,
* giving a faster execution.
*/
Class<? extends QuarkusTestProfile> value();

}

0 comments on commit 0501a5d

Please sign in to comment.