diff --git a/integration-tests/devmode/pom.xml b/integration-tests/devmode/pom.xml
index f149a3b7510d3..61ff9b3365fc3 100644
--- a/integration-tests/devmode/pom.xml
+++ b/integration-tests/devmode/pom.xml
@@ -84,6 +84,11 @@
quarkus-junit5-internal
test
+
+ io.quarkus
+ quarkus-junit5-component
+ test
+
io.rest-assured
rest-assured
diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentContinuousTestingTest.java b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentContinuousTestingTest.java
new file mode 100644
index 0000000000000..7bbc7ccb4698a
--- /dev/null
+++ b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentContinuousTestingTest.java
@@ -0,0 +1,41 @@
+package io.quarkus.test.component;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.RegisterExtension;
+
+import io.quarkus.test.ContinuousTestingTestUtils;
+import io.quarkus.test.ContinuousTestingTestUtils.TestStatus;
+import io.quarkus.test.QuarkusDevModeTest;
+
+public class ComponentContinuousTestingTest {
+
+ @RegisterExtension
+ static final QuarkusDevModeTest config = new QuarkusDevModeTest()
+ .withApplicationRoot(
+ root -> root.addClass(ComponentFoo.class).add(new StringAsset(ContinuousTestingTestUtils.appProperties()),
+ "application.properties"))
+ .setTestArchiveProducer(() -> ShrinkWrap.create(JavaArchive.class)
+ .addClass(ComponentUT.class));
+
+ @Test
+ public void test() {
+ ContinuousTestingTestUtils utils = new ContinuousTestingTestUtils();
+ TestStatus ts = utils.waitForNextCompletion();
+ assertEquals(0L, ts.getTestsFailed());
+ assertEquals(1L, ts.getTestsPassed());
+ assertEquals(0L, ts.getTestsSkipped());
+
+ config.modifySourceFile(ComponentFoo.class, s -> s.replace("return bar;", "return bar + bar;"));
+
+ ts = utils.waitForNextCompletion();
+ assertEquals(1L, ts.getTestsFailed());
+ assertEquals(0L, ts.getTestsPassed());
+ assertEquals(0L, ts.getTestsSkipped());
+ }
+
+}
diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentFoo.java b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentFoo.java
new file mode 100644
index 0000000000000..5b12c6a524fe3
--- /dev/null
+++ b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentFoo.java
@@ -0,0 +1,17 @@
+package io.quarkus.test.component;
+
+import jakarta.inject.Singleton;
+
+import org.eclipse.microprofile.config.inject.ConfigProperty;
+
+@Singleton
+public class ComponentFoo {
+
+ @ConfigProperty(name = "bar", defaultValue = "baz")
+ String bar;
+
+ String ping() {
+ return bar;
+ }
+
+}
diff --git a/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentUT.java b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentUT.java
new file mode 100644
index 0000000000000..d6a6e4302ed5d
--- /dev/null
+++ b/integration-tests/devmode/src/test/java/io/quarkus/test/component/ComponentUT.java
@@ -0,0 +1,21 @@
+package io.quarkus.test.component;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import jakarta.inject.Inject;
+
+import org.junit.jupiter.api.Test;
+
+@QuarkusComponentTest
+@TestConfigProperty(key = "bar", value = "qux")
+public class ComponentUT {
+
+ @Inject
+ ComponentFoo foo;
+
+ @Test
+ public void test() {
+ assertEquals("qux", foo.ping());
+ }
+
+}
diff --git a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java
index a5eaebcb5c164..00960605b882a 100644
--- a/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java
+++ b/test-framework/junit5-component/src/main/java/io/quarkus/test/component/QuarkusComponentTestExtension.java
@@ -505,6 +505,9 @@ private ClassLoader initArcContainer(ExtensionContext extensionContext, Collecti
throw new IllegalStateException("Failed to create index", e);
}
+ ClassLoader testClassClassLoader = testClass.getClassLoader();
+ // The test class is loaded by the QuarkusClassLoader in continuous testing environment
+ boolean isContinuousTesting = testClassClassLoader instanceof QuarkusClassLoader;
ClassLoader oldTccl = Thread.currentThread().getContextClassLoader();
IndexView computingIndex = BeanArchives.buildComputingBeanArchiveIndex(oldTccl,
@@ -541,11 +544,13 @@ private ClassLoader initArcContainer(ExtensionContext extensionContext, Collecti
// We need collect all generated resources so that we can remove them after the test
// NOTE: previously we kept the generated framework classes (to speedup subsequent test runs) but that breaks the existing @QuarkusTests
- Set generatedResources = new HashSet<>();
+ Set generatedResources;
+ // E.g. target/generated-arc-sources/org/acme/ComponentsProvider
File componentsProviderFile = getComponentsProviderFile(testClass);
- if (testClass.getClassLoader() instanceof QuarkusClassLoader) {
- //continuous testing environment
+
+ if (isContinuousTesting) {
+ generatedResources = Set.of();
Map classes = new HashMap<>();
builder.setOutput(new ResourceOutput() {
@Override
@@ -566,11 +571,12 @@ public void writeResource(Resource resource) throws IOException {
}
break;
default:
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Unsupported resource type: " + resource.getType());
}
}
});
} else {
+ generatedResources = new HashSet<>();
File testOutputDirectory = getTestOutputDirectory(testClass);
builder.setOutput(new ResourceOutput() {
@Override
@@ -590,7 +596,7 @@ public void writeResource(Resource resource) throws IOException {
}
break;
default:
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Unsupported resource type: " + resource.getType());
}
}
});
@@ -748,7 +754,9 @@ public void accept(BytecodeTransformer transformer) {
}
// Use a custom ClassLoader to load the generated ComponentsProvider file
- QuarkusComponentTestClassLoader testClassLoader = new QuarkusComponentTestClassLoader(oldTccl,
+ // In continuous testing the CL that loaded the test class must be used as the parent CL
+ QuarkusComponentTestClassLoader testClassLoader = new QuarkusComponentTestClassLoader(
+ isContinuousTesting ? testClassClassLoader : oldTccl,
componentsProviderFile,
null);
Thread.currentThread().setContextClassLoader(testClassLoader);
@@ -1077,18 +1085,18 @@ private File getComponentsProviderFile(Class> testClass) {
File targetDir = new File("target");
if (targetDir.canWrite()) {
// maven build
- generatedSourcesDirectory = new File("target/generated-arc-sources");
+ generatedSourcesDirectory = new File(targetDir, "generated-arc-sources");
} else {
File buildDir = new File("build");
if (buildDir.canWrite()) {
// gradle build
- generatedSourcesDirectory = new File("build/generated-arc-sources");
+ generatedSourcesDirectory = new File(buildDir, "generated-arc-sources");
} else {
generatedSourcesDirectory = new File("quarkus-component-test/generated-arc-sources");
}
}
- return new File(generatedSourcesDirectory,
- nameToPath(testClass.getPackage().getName()) + File.pathSeparator + ComponentsProvider.class.getSimpleName());
+ return new File(new File(generatedSourcesDirectory, nameToPath(testClass.getPackage().getName())),
+ ComponentsProvider.class.getSimpleName());
}
}