diff --git a/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionMultiDeploymentTest.java b/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionMultiDeploymentTest.java
new file mode 100644
index 000000000..13cc96758
--- /dev/null
+++ b/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionMultiDeploymentTest.java
@@ -0,0 +1,111 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2025 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.arquillian.integration.test.manual;
+
+import java.net.URI;
+import java.net.URL;
+
+import org.jboss.arquillian.container.test.api.ContainerController;
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.jboss.arquillian.integration.test.common.app.EchoResource;
+import org.jboss.arquillian.integration.test.common.app.RestActivator;
+import org.jboss.arquillian.junit5.container.annotation.ArquillianTest;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author James R. Perkins
+ */
+@ArquillianTest
+@RunAsClient
+public class ManualModeInjectionMultiDeploymentTest {
+ private static final String CONTAINER_NAME = "default";
+ static final String DEPLOYMENT_NAME_1 = "manual-mode-default";
+ static final String DEPLOYMENT_NAME_2 = "manual-mode-secondary";
+
+ @ArquillianResource
+ private static ContainerController controller;
+
+ @ArquillianResource
+ private static Deployer deployer;
+
+ @Deployment(name = DEPLOYMENT_NAME_1, managed = false)
+ public static WebArchive createDeployment() {
+ return ShrinkWrap.create(WebArchive.class, DEPLOYMENT_NAME_1 + ".war")
+ .addClasses(RestActivator.class, EchoResource.class)
+ .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
+ }
+
+ @Deployment(name = DEPLOYMENT_NAME_2, managed = false)
+ public static WebArchive createDeploymentSecondary() {
+ return ShrinkWrap.create(WebArchive.class, DEPLOYMENT_NAME_2 + ".war")
+ .addClasses(RestActivator.class, EchoResource.class)
+ .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
+ }
+
+ @BeforeEach
+ public void startAndDeploy() {
+ controller.start(CONTAINER_NAME);
+ deployer.deploy(DEPLOYMENT_NAME_1);
+ deployer.deploy(DEPLOYMENT_NAME_2);
+ }
+
+ @AfterEach
+ public void stop() {
+ if (controller.isStarted(CONTAINER_NAME)) {
+ deployer.undeploy(DEPLOYMENT_NAME_1);
+ deployer.undeploy(DEPLOYMENT_NAME_2);
+ controller.stop(CONTAINER_NAME);
+ }
+ }
+
+ @Test
+ public void checkUri(@OperateOnDeployment(DEPLOYMENT_NAME_1) @ArquillianResource final URI uri) {
+ Assertions.assertNotNull(uri);
+ Assertions.assertTrue(uri.toString().contains("/" + DEPLOYMENT_NAME_1));
+ }
+
+ @Test
+ public void checkUrl(@OperateOnDeployment(DEPLOYMENT_NAME_1) @ArquillianResource final URL url) {
+ Assertions.assertNotNull(url);
+ Assertions.assertTrue(url.toString().contains("/" + DEPLOYMENT_NAME_1));
+ }
+
+ @Test
+ public void checkSecondaryUri(@OperateOnDeployment(DEPLOYMENT_NAME_2) @ArquillianResource final URI uri) {
+ Assertions.assertNotNull(uri);
+ Assertions.assertTrue(uri.toString().contains("/" + DEPLOYMENT_NAME_2));
+ }
+
+ @Test
+ public void checkSecondaryUrl(@OperateOnDeployment(DEPLOYMENT_NAME_2) @ArquillianResource final URL url) {
+ Assertions.assertNotNull(url);
+ Assertions.assertTrue(url.toString().contains("/" + DEPLOYMENT_NAME_2));
+ }
+}
diff --git a/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionTest.java b/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionTest.java
new file mode 100644
index 000000000..3343612cf
--- /dev/null
+++ b/integration-tests/junit5-tests/src/test/java/org/jboss/arquillian/integration/test/manual/ManualModeInjectionTest.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2025 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.arquillian.integration.test.manual;
+
+import java.net.URI;
+import java.net.URL;
+
+import org.jboss.arquillian.container.test.api.ContainerController;
+import org.jboss.arquillian.container.test.api.Deployer;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.jboss.arquillian.integration.test.common.app.EchoResource;
+import org.jboss.arquillian.integration.test.common.app.RestActivator;
+import org.jboss.arquillian.junit5.container.annotation.ArquillianTest;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.EmptyAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+/**
+ * @author James R. Perkins
+ */
+@ArquillianTest
+@RunAsClient
+public class ManualModeInjectionTest {
+ private static final String CONTAINER_NAME = "default";
+ static final String DEPLOYMENT_NAME = "manual-mode";
+
+ @ArquillianResource
+ private static ContainerController controller;
+
+ @ArquillianResource
+ private static Deployer deployer;
+
+ @Deployment(name = DEPLOYMENT_NAME, managed = false)
+ public static WebArchive createDeployment() {
+ return ShrinkWrap.create(WebArchive.class, DEPLOYMENT_NAME + ".war")
+ .addClasses(RestActivator.class, EchoResource.class)
+ .addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
+ }
+
+ @BeforeEach
+ public void startAndDeploy() {
+ controller.start(CONTAINER_NAME);
+ deployer.deploy(DEPLOYMENT_NAME);
+ }
+
+ @AfterEach
+ public void stop() {
+ if (controller.isStarted(CONTAINER_NAME)) {
+ deployer.undeploy(DEPLOYMENT_NAME);
+ controller.stop(CONTAINER_NAME);
+ }
+ }
+
+ @Test
+ public void checkUri(@ArquillianResource final URI uri) {
+ Assertions.assertNotNull(uri);
+ Assertions.assertTrue(uri.toString().contains("/" + DEPLOYMENT_NAME));
+ }
+
+ @Test
+ public void checkUrl(@ArquillianResource final URL url) {
+ Assertions.assertNotNull(url);
+ Assertions.assertTrue(url.toString().contains("/" + DEPLOYMENT_NAME));
+ }
+}
diff --git a/junit5/core/pom.xml b/junit5/core/pom.xml
index 28448112c..9b2683f45 100644
--- a/junit5/core/pom.xml
+++ b/junit5/core/pom.xml
@@ -26,6 +26,11 @@
arquillian-test-spi
${project.version}
+
+ org.jboss.arquillian.container
+ arquillian-container-spi
+ ${project.version}
+
org.junit.jupiter
diff --git a/junit5/core/src/main/java/org/jboss/arquillian/junit5/ArquillianExtension.java b/junit5/core/src/main/java/org/jboss/arquillian/junit5/ArquillianExtension.java
index 9da2c3828..caecff3e8 100644
--- a/junit5/core/src/main/java/org/jboss/arquillian/junit5/ArquillianExtension.java
+++ b/junit5/core/src/main/java/org/jboss/arquillian/junit5/ArquillianExtension.java
@@ -14,6 +14,7 @@
import org.junit.jupiter.api.extension.AfterEachCallback;
import org.junit.jupiter.api.extension.BeforeAllCallback;
import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.BeforeTestExecutionCallback;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.api.extension.InvocationInterceptor;
import org.junit.jupiter.api.extension.ParameterContext;
@@ -27,7 +28,7 @@
import static org.jboss.arquillian.junit5.ContextStore.getContextStore;
import static org.jboss.arquillian.junit5.JUnitJupiterTestClassLifecycleManager.getManager;
-public class ArquillianExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, InvocationInterceptor, TestExecutionExceptionHandler, ParameterResolver {
+public class ArquillianExtension implements BeforeAllCallback, AfterAllCallback, BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, InvocationInterceptor, TestExecutionExceptionHandler, ParameterResolver {
public static final String RUNNING_INSIDE_ARQUILLIAN = "insideArquillian";
private static final String CHAIN_EXCEPTION_MESSAGE_PREFIX = "Chain of InvocationInterceptors never called invocation";
@@ -78,6 +79,16 @@ public void afterEach(ExtensionContext context) throws Exception {
}
}
+ @Override
+ public void beforeTestExecution(final ExtensionContext context) throws Exception {
+ // Get the adapter, test instance and method
+ final TestRunnerAdaptor adapter = getManager(context)
+ .getAdaptor();
+ final Object instance = context.getRequiredTestInstance();
+ final Method method = context.getRequiredTestMethod();
+ adapter.fireCustomLifecycle(new BeforeTestExecutionEvent(instance, method));
+ }
+
@Override
public void interceptTestTemplateMethod(Invocation invocation, ReflectiveInvocationContext invocationContext, ExtensionContext extensionContext) throws Throwable {
if (IS_INSIDE_ARQUILLIAN.test(extensionContext)) {
diff --git a/junit5/core/src/main/java/org/jboss/arquillian/junit5/BeforeTestExecutionEvent.java b/junit5/core/src/main/java/org/jboss/arquillian/junit5/BeforeTestExecutionEvent.java
new file mode 100644
index 000000000..e7f94224b
--- /dev/null
+++ b/junit5/core/src/main/java/org/jboss/arquillian/junit5/BeforeTestExecutionEvent.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ *
+ * Copyright 2025 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jboss.arquillian.junit5;
+
+import java.lang.reflect.Method;
+
+import org.jboss.arquillian.test.spi.event.suite.TestLifecycleEvent;
+
+/**
+ * An event triggered before each test is invoked.
+ *
+ * @author James R. Perkins
+ */
+public class BeforeTestExecutionEvent extends TestLifecycleEvent {
+ /**
+ * Creates a new event.
+ *
+ * @param testInstance The test case instance
+ * @param testMethod The test method
+ *
+ * @throws IllegalArgumentException if testInstance is null
+ * @throws IllegalArgumentException if testMethod is null
+ */
+ BeforeTestExecutionEvent(final Object testInstance, final Method testMethod) {
+ super(testInstance, testMethod);
+ }
+}
diff --git a/junit5/core/src/main/java/org/jboss/arquillian/junit5/MethodParameterObserver.java b/junit5/core/src/main/java/org/jboss/arquillian/junit5/MethodParameterObserver.java
index e9d015921..a61d345f0 100644
--- a/junit5/core/src/main/java/org/jboss/arquillian/junit5/MethodParameterObserver.java
+++ b/junit5/core/src/main/java/org/jboss/arquillian/junit5/MethodParameterObserver.java
@@ -21,7 +21,11 @@
import java.lang.reflect.Method;
import java.util.Collection;
+import java.util.stream.Collectors;
+import org.jboss.arquillian.container.spi.client.deployment.Deployment;
+import org.jboss.arquillian.container.spi.client.deployment.DeploymentScenario;
+import org.jboss.arquillian.container.spi.context.DeploymentContext;
import org.jboss.arquillian.core.api.Event;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.InstanceProducer;
@@ -33,7 +37,6 @@
import org.jboss.arquillian.test.spi.event.enrichment.AfterEnrichment;
import org.jboss.arquillian.test.spi.event.enrichment.BeforeEnrichment;
import org.jboss.arquillian.test.spi.event.enrichment.EnrichmentEvent;
-import org.jboss.arquillian.test.spi.event.suite.Before;
/**
* The observer used to process method parameters provided by Arquillian.
@@ -52,26 +55,39 @@ public class MethodParameterObserver {
@TestScoped
private InstanceProducer methodParametersProducer;
+ @Inject
+ private Instance deploymentContext;
+
+ @Inject
+ private Instance deploymentScenario;
+
/**
* Updates the stored {@link MethodParameters} for method parameters which can be provided by Arquillian.
*
* @param event the fired event
*/
- public void injectParameters(@Observes final Before event) {
- final Object testInstance = event.getTestInstance();
- final Method testMethod = event.getTestMethod();
- enrichmentEvent.fire(new BeforeEnrichment(testInstance, testMethod));
- final MethodParameters methodParameters = methodParametersProducer.get();
- final Collection testEnrichers = serviceLoader.get().all(TestEnricher.class);
- for (TestEnricher enricher : testEnrichers) {
- final Object[] values = enricher.resolve(testMethod);
- for (int i = 0; i < values.length; i++) {
- if (values[i] != null) {
- methodParameters.add(i, values[i]);
+ public void injectParameters(@Observes final BeforeTestExecutionEvent event) {
+ final boolean contextActivated = activateDeployments();
+ try {
+ final Object testInstance = event.getTestInstance();
+ final Method testMethod = event.getTestMethod();
+ enrichmentEvent.fire(new BeforeEnrichment(testInstance, testMethod));
+ final MethodParameters methodParameters = methodParametersProducer.get();
+ final Collection testEnrichers = serviceLoader.get().all(TestEnricher.class);
+ for (TestEnricher enricher : testEnrichers) {
+ final Object[] values = enricher.resolve(testMethod);
+ for (int i = 0; i < values.length; i++) {
+ if (values[i] != null) {
+ methodParameters.add(i, values[i]);
+ }
}
}
+ enrichmentEvent.fire(new AfterEnrichment(testEnrichers, testMethod));
+ } finally {
+ if (contextActivated) {
+ deploymentContext.get().deactivate();
+ }
}
- enrichmentEvent.fire(new AfterEnrichment(testEnrichers, testMethod));
}
/**
@@ -82,4 +98,21 @@ public void injectParameters(@Observes final Before event) {
public void injectParameters(@Observes MethodParameterProducerEvent event) {
methodParametersProducer.set(event.getTestParameterHolder());
}
+
+ private boolean activateDeployments() {
+ final DeploymentContext context = deploymentContext.get();
+ // If the deployment context is not available or already active, we don't need to activate the deployment context
+ if (context == null || context.isActive()) {
+ return false;
+ }
+ final Collection activeDeployments = deploymentScenario.get().deployments().stream()
+ .filter(Deployment::isDeployed)
+ .collect(Collectors.toList());
+ // If there are multiple deployments, don't activate any of them as an @OperatesOnDeployment should be used
+ if (activeDeployments.size() != 1) {
+ return false;
+ }
+ activeDeployments.forEach(context::activate);
+ return true;
+ }
}