Make sure that src/test/resources takes precidence for @QuarkusTest

This allows for easy config overriding in tests
This commit is contained in:
Stuart Douglas
2020-02-07 12:23:41 +11:00
parent d9d1d8ffc9
commit 3a909ccdf8
8 changed files with 126 additions and 16 deletions

View File

@@ -30,10 +30,21 @@ public class AdditionalDependency implements Serializable {
*/
private final boolean forceApplicationArchive;
/**
* If this dep is the test classes directory. If so then this will have precedence over the application
*/
private final boolean testClassRoot;
public AdditionalDependency(Path archivePath, boolean hotReloadable, boolean forceApplicationArchive) {
this(archivePath, hotReloadable, forceApplicationArchive, false);
}
public AdditionalDependency(Path archivePath, boolean hotReloadable, boolean forceApplicationArchive,
boolean testClassRoot) {
this.archivePath = archivePath;
this.hotReloadable = hotReloadable;
this.forceApplicationArchive = forceApplicationArchive;
this.testClassRoot = testClassRoot;
}
public Path getArchivePath() {
@@ -47,4 +58,8 @@ public class AdditionalDependency implements Serializable {
public boolean isForceApplicationArchive() {
return forceApplicationArchive;
}
public boolean isTestClassRoot() {
return testClassRoot;
}
}

View File

@@ -1,7 +1,6 @@
package io.quarkus.bootstrap.app;
import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -113,7 +112,8 @@ public class CuratedApplication implements Serializable, Closeable {
public AugmentAction createAugmentor(String functionName, Map<String, Object> props) {
try {
Class<?> augmentor = getAugmentClassLoader().loadClass(AUGMENTOR);
Function<Object, List<?>> function = (Function<Object, List<?>>) getAugmentClassLoader().loadClass(functionName).newInstance();
Function<Object, List<?>> function = (Function<Object, List<?>>) getAugmentClassLoader().loadClass(functionName)
.newInstance();
List<?> res = function.apply(props);
return (AugmentAction) augmentor.getConstructor(CuratedApplication.class, List.class).newInstance(this, res);
} catch (Exception e) {
@@ -224,18 +224,27 @@ public class CuratedApplication implements Serializable, Closeable {
QuarkusClassLoader.Builder builder = QuarkusClassLoader.builder("Quarkus Base Runtime ClassLoader",
quarkusBootstrap.getBaseClassLoader(), false);
if (quarkusBootstrap.getMode() == QuarkusBootstrap.Mode.TEST) {
//in test mode we have everything in the base class loader
//there is no need to restart so there is no need for an additional CL
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
//src/test is the highest priority
if (i.isTestClassRoot()) {
builder.addElement(ClassPathElement.fromPath(i.getArchivePath()));
}
}
builder.addElement(ClassPathElement.fromPath(getQuarkusBootstrap().getApplicationRoot()));
}
//additional user class path elements first
Set<Path> hotReloadPaths = new HashSet<>();
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
if (!i.isHotReloadable()) {
builder.addElement(ClassPathElement.fromPath(i.getArchivePath()));
} else {
hotReloadPaths.add(i.getArchivePath());
if (!i.isTestClassRoot()) {
if (!i.isHotReloadable()) {
builder.addElement(ClassPathElement.fromPath(i.getArchivePath()));
} else {
hotReloadPaths.add(i.getArchivePath());
}
}
}
builder.setResettableElement(new MemoryClassPathElement(Collections.emptyMap()));
@@ -264,20 +273,26 @@ public class CuratedApplication implements Serializable, Closeable {
QuarkusClassLoader.Builder builder = QuarkusClassLoader.builder("Deployment Class Loader",
getAugmentClassLoader(), false)
.setAggregateParentResources(true);
//add the application root
//add the application root, and test roots
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
if (i.isTestClassRoot()) {
builder.addElement(ClassPathElement.fromPath(i.getArchivePath()));
}
}
builder.addElement(ClassPathElement.fromPath(quarkusBootstrap.getApplicationRoot()));
//additional user class path elements first
for (AdditionalDependency i : quarkusBootstrap.getAdditionalApplicationArchives()) {
builder.addElement(ClassPathElement.fromPath(i.getArchivePath()));
if (!i.isTestClassRoot()) {
builder.addElement(ClassPathElement.fromPath(i.getArchivePath()));
}
}
return builder.build();
}
public QuarkusClassLoader createRuntimeClassLoader(QuarkusClassLoader loader,
Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> bytecodeTransformers,
ClassLoader deploymentClassLoader, Map<String, byte[]> resources) {
Map<String, List<BiFunction<String, ClassVisitor, ClassVisitor>>> bytecodeTransformers,
ClassLoader deploymentClassLoader, Map<String, byte[]> resources) {
QuarkusClassLoader.Builder builder = QuarkusClassLoader.builder("Quarkus Runtime ClassLoader",
loader, false)
.setAggregateParentResources(true);
@@ -296,10 +311,10 @@ public class CuratedApplication implements Serializable, Closeable {
@Override
public void close() {
if(augmentClassLoader != null) {
if (augmentClassLoader != null) {
augmentClassLoader.close();
}
if(baseRuntimeClassLoader != null) {
if (baseRuntimeClassLoader != null) {
baseRuntimeClassLoader.close();
}
}

View File

@@ -0,0 +1,19 @@
package io.quarkus.it.resteasy.jackson;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@Path("/message")
public class MessageResource {
@ConfigProperty(name = "message")
String message;
@GET
public String message() {
return message;
}
}

View File

@@ -0,0 +1 @@
message=Production

View File

@@ -0,0 +1,28 @@
package io.quarkus.it.resteasy.jackson;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;
import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.NativeImageTest;
/**
* tests that application.properties is read from src/main/resources when running native image tests
*
* This does not necessarily belong here, but main and test-extension have a lot of existing
* config that would need to be duplicated, so it is here out of convenience.
*/
@NativeImageTest
class ApplicationPropertiesOverrideIT {
@Test
void testEndpoint() {
given()
.when().get("/message")
.then()
.statusCode(200)
.body(containsString("Production"));
}
}

View File

@@ -0,0 +1,28 @@
package io.quarkus.it.resteasy.jackson;
import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.containsString;
import org.junit.jupiter.api.Test;
import io.quarkus.test.junit.QuarkusTest;
/**
* tests that application.properties is read from src/test/resources
*
* This does not necessarily belong here, but main and test-extension have a lot of existing
* config that would need to be duplicated, so it is here out of convenience.
*/
@QuarkusTest
class ApplicationPropertiesOverrideTest {
@Test
void testEndpoint() {
given()
.when().get("/message")
.then()
.statusCode(200)
.body(containsString("Test"));
}
}

View File

@@ -0,0 +1 @@
message=Test

View File

@@ -80,7 +80,7 @@ public class QuarkusTestExtension
testClassLocation = getTestClassesLocation(context.getRequiredTestClass());
if (!appClassLocation.equals(testClassLocation)) {
runnerBuilder.addAdditionalApplicationArchive(new AdditionalDependency(testClassLocation, false, true));
runnerBuilder.addAdditionalApplicationArchive(new AdditionalDependency(testClassLocation, false, true, true));
}
CuratedApplication curatedApplication = runnerBuilder
.setTest(true)
@@ -171,7 +171,7 @@ public class QuarkusTestExtension
ExtensionContext root = extensionContext.getRoot();
ExtensionContext.Store store = root.getStore(ExtensionContext.Namespace.GLOBAL);
ExtensionState state = store.get(ExtensionState.class.getName(), ExtensionState.class);
if (state == null) {
if (state == null && !failedBoot) {
PropertyTestUtil.setLogFileProperty();
TestResourceManager testResourceManager = new TestResourceManager(extensionContext.getRequiredTestClass());
try {
@@ -261,6 +261,9 @@ public class QuarkusTestExtension
Thread.currentThread().setContextClassLoader(old);
}
ExtensionState state = ensureStarted(extensionContext);
if (failedBoot) {
return invocation.proceed();
}
initTestState(extensionContext, state);
return result;
}