diff --git a/README.adoc b/README.adoc
index 31d0a7d0c..d690a71a9 100644
--- a/README.adoc
+++ b/README.adoc
@@ -33,7 +33,7 @@ Arquillian brings the test to the runtime so you don’t have to manage the runt
* Executing the tests inside (or against) the container
* Capturing the results and returning them to the test runner for reporting
-To avoid introducing unnecessary complexity into the developer’s build environment, Arquillian integrates seamlessly with familiar testing frameworks (e.g., JUnit 4, TestNG 5), allowing tests to be launched using existing IDE, Ant and Maven test plugins — without any add-ons.
+To avoid introducing unnecessary complexity into the developer’s build environment, Arquillian integrates seamlessly with familiar testing frameworks (e.g., JUnit 4, JUnit 5, TestNG 5), allowing tests to be launched using existing IDE, Ant and Maven test plugins — without any add-ons.
ifdef::generated-doc[]
== Guide
diff --git a/junit5/container/pom.xml b/junit5/container/pom.xml
index d3565f0d3..ed93698a6 100644
--- a/junit5/container/pom.xml
+++ b/junit5/container/pom.xml
@@ -91,5 +91,11 @@
provided
+
+ org.mockito
+ mockito-all
+ test
+
+
diff --git a/junit5/container/src/main/java/org/jboss/arquillian/junit5/container/JUnitJupiterTestRunner.java b/junit5/container/src/main/java/org/jboss/arquillian/junit5/container/JUnitJupiterTestRunner.java
index 2e8ccd4c5..23ea4d07d 100644
--- a/junit5/container/src/main/java/org/jboss/arquillian/junit5/container/JUnitJupiterTestRunner.java
+++ b/junit5/container/src/main/java/org/jboss/arquillian/junit5/container/JUnitJupiterTestRunner.java
@@ -92,6 +92,8 @@ public void executionFinished(TestIdentifier testIdentifier, TestExecutionResult
testExecutionResult.getThrowable().orElseGet(() -> new TestAbortedException("Aborted"))
);
break;
+ case SUCCESSFUL:
+ break;
}
}
diff --git a/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/ClassWithArquillianExtensionWithExtensions.java b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/ClassWithArquillianExtensionWithExtensions.java
new file mode 100644
index 000000000..bc2fcebcc
--- /dev/null
+++ b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/ClassWithArquillianExtensionWithExtensions.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2021 Red Hat Inc. and/or its affiliates and other contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.container;
+
+import static org.jboss.arquillian.junit5.container.JUnitTestBaseClass.Cycle;
+import static org.jboss.arquillian.junit5.container.JUnitTestBaseClass.wasCalled;
+
+import org.jboss.arquillian.junit5.ArquillianExtension;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.AfterAllCallback;
+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.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+
+@ExtendWith(ArquillianExtension.class)
+@ExtendWith(ClassWithArquillianExtensionWithExtensions.MethodRule.class)
+@ExtendWith(ClassWithArquillianExtensionWithExtensions.ClassRule.class)
+public class ClassWithArquillianExtensionWithExtensions {
+
+ public static class ClassRule implements AfterAllCallback, BeforeAllCallback {
+ @Override
+ public void afterAll(ExtensionContext context) throws Exception {
+ wasCalled(Cycle.AFTER_CLASS_RULE);
+ }
+
+ @Override
+ public void beforeAll(ExtensionContext context) throws Exception {
+ wasCalled(Cycle.BEFORE_CLASS_RULE);
+ }
+ }
+
+ public static class MethodRule implements AfterEachCallback, BeforeEachCallback {
+ @Override
+ public void afterEach(ExtensionContext context) throws Exception {
+ wasCalled(Cycle.AFTER_RULE);
+ }
+
+ @Override
+ public void beforeEach(ExtensionContext context) throws Exception {
+ wasCalled(Cycle.BEFORE_RULE);
+ }
+ }
+
+ @BeforeAll
+ public static void beforeClass() throws Throwable {
+ wasCalled(Cycle.BEFORE_CLASS);
+ }
+
+ @AfterAll
+ public static void afterClass() throws Throwable {
+ wasCalled(Cycle.AFTER_CLASS);
+ }
+
+ @BeforeEach
+ public void before() throws Throwable {
+ wasCalled(Cycle.BEFORE);
+ }
+
+ @AfterEach
+ public void after() throws Throwable {
+ wasCalled(Cycle.AFTER);
+ }
+
+ @Test
+ public void shouldBeInvoked() throws Throwable {
+ wasCalled(Cycle.TEST);
+ }
+}
diff --git a/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitIntegrationTestCase.java b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitIntegrationTestCase.java
new file mode 100644
index 000000000..6a2ed8823
--- /dev/null
+++ b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitIntegrationTestCase.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2021 Red Hat Inc. and/or its affiliates and other contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.container;
+
+import org.jboss.arquillian.test.spi.TestRunnerAdaptor;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.junit.platform.launcher.listeners.TestExecutionSummary;
+
+import static org.mockito.Mockito.mock;
+
+public class JUnitIntegrationTestCase extends JUnitTestBaseClass {
+
+ @Test
+ public void shouldExecuteExtensions() throws Exception {
+ // given
+ TestRunnerAdaptor adaptor = mock(TestRunnerAdaptor.class);
+ executeAllLifeCycles(adaptor);
+
+ // when
+ TestExecutionSummary result = run(adaptor, ClassWithArquillianExtensionWithExtensions.class);
+
+ // then
+ Assertions.assertEquals(1, result.getTestsSucceededCount());
+ Assertions.assertEquals(0, result.getTestsFailedCount());
+ Assertions.assertEquals(0, result.getTestsSkippedCount());
+ assertCycle(1, Cycle.BEFORE_RULE, Cycle.BEFORE_CLASS, Cycle.BEFORE, Cycle.TEST, Cycle.AFTER, Cycle.AFTER_CLASS,
+ Cycle.AFTER_RULE, Cycle.BEFORE_CLASS_RULE, Cycle.AFTER_CLASS_RULE);
+ }
+}
diff --git a/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitJupiterDeploymentAppenderTestCase.java b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitJupiterDeploymentAppenderTestCase.java
new file mode 100644
index 000000000..db635a6ca
--- /dev/null
+++ b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitJupiterDeploymentAppenderTestCase.java
@@ -0,0 +1,44 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2021 Red Hat Inc. and/or its affiliates and other contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.container;
+
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.ArchivePaths;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * JUnitJupiterDeploymentAppenderTestCase
+ *
+ * @author Aslak Knutsen
+ * @version $Revision: $
+ */
+public class JUnitJupiterDeploymentAppenderTestCase {
+
+ @Test
+ public void shouldGenerateDependencies() throws Exception {
+ Archive> archive = new JUnitJupiterDeploymentAppender().createAuxiliaryArchive();
+
+ Assertions.assertTrue(
+ archive.contains(ArchivePaths.create("/META-INF/services/org.jboss.arquillian.container.test.spi.TestRunner")),
+ "Should have added Extension");
+
+ Assertions.assertTrue(
+ archive.contains(ArchivePaths.create("/org/jboss/arquillian/junit5/container/JUnitJupiterTestRunner.class")),
+ "Should have added TestRunner Impl");
+ }
+}
diff --git a/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitJupiterTestRunnerTestCase.java b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitJupiterTestRunnerTestCase.java
new file mode 100644
index 000000000..ef421e98d
--- /dev/null
+++ b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitJupiterTestRunnerTestCase.java
@@ -0,0 +1,113 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2021 Red Hat Inc. and/or its affiliates and other contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.container;
+
+import org.jboss.arquillian.junit5.IdentifiedTestException;
+import org.jboss.arquillian.test.spi.TestResult;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class JUnitJupiterTestRunnerTestCase {
+
+ @Test
+ public void shouldReturnExceptionToClientIfFailingOnWrongExceptionThrown() throws Exception {
+ JUnitJupiterTestRunner runner = new JUnitJupiterTestRunner();
+ TestResult result = runner.execute(TestScenarios.class, "shouldFailExpectedWrongException");
+
+ Assertions.assertEquals(TestResult.Status.FAILED, result.getStatus());
+ Assertions.assertNotNull(result.getThrowable());
+ Assertions.assertEquals(IdentifiedTestException.class, result.getThrowable().getClass());
+ }
+
+ @Test
+ public void shouldNotReturnExceptionToClientIfAsumptionPassing() throws Exception {
+ JUnitJupiterTestRunner runner = new JUnitJupiterTestRunner();
+ TestResult result = runner.execute(TestScenarios.class, "shouldPassOnAssumption");
+
+ Assertions.assertEquals(TestResult.Status.PASSED, result.getStatus());
+ Assertions.assertNull(result.getThrowable());
+ }
+
+ @Test
+ public void shouldReturnExceptionToClientIfAsumptionFailing() throws Exception {
+ JUnitJupiterTestRunner runner = new JUnitJupiterTestRunner();
+ TestResult result = runner.execute(TestScenarios.class, "shouldSkipOnAssumption");
+
+ Assertions.assertEquals(TestResult.Status.FAILED, result.getStatus());
+ Assertions.assertNotNull(result.getThrowable());
+ Assertions.assertEquals(IdentifiedTestException.class, result.getThrowable().getClass());
+ }
+
+ @Test
+ public void shouldNotReturnExceptionToClientIfExpectedRulePassing() throws Exception {
+ JUnitJupiterTestRunner runner = new JUnitJupiterTestRunner();
+ TestResult result = runner.execute(TestScenarios.class, "shouldPassOnException");
+
+ Assertions.assertEquals(TestResult.Status.PASSED, result.getStatus());
+ Assertions.assertNull(result.getThrowable());
+ }
+
+ @Test
+ public void shouldReturnExceptionToClientIfExpectedRuleFailing() throws Exception {
+ JUnitJupiterTestRunner runner = new JUnitJupiterTestRunner();
+ TestResult result = runner.execute(TestScenarios.class, "shouldFailOnException");
+
+ Assertions.assertEquals(TestResult.Status.FAILED, result.getStatus());
+ Assertions.assertNotNull(result.getThrowable());
+ Assertions.assertEquals(IdentifiedTestException.class, result.getThrowable().getClass());
+ }
+
+ @Test
+ public void shouldReturnExceptionThrownInBeforeToClientWhenTestFails() throws Exception {
+ Exception expectedException = new Exception("Expected");
+ TestScenarios.exceptionThrownInBefore = expectedException;
+
+ Exception unexpectedException = new Exception("Not expected");
+ TestScenarios.exceptionThrownInAfter = unexpectedException;
+
+ JUnitJupiterTestRunner runner = new JUnitJupiterTestRunner();
+ TestResult result = runner.execute(TestScenarios.class, "shouldFailOnException");
+
+ Assertions.assertEquals(TestResult.Status.FAILED, result.getStatus());
+ Assertions.assertEquals(IdentifiedTestException.class, result.getThrowable().getClass());
+ }
+
+ @Test
+ public void shouldReturnAssertionErrorToClientWhenAfterThrowsException() throws Exception {
+ Exception unexpectedException = new Exception("Not expected");
+ TestScenarios.exceptionThrownInAfter = unexpectedException;
+
+ JUnitJupiterTestRunner runner = new JUnitJupiterTestRunner();
+ TestResult result = runner.execute(TestScenarios.class, "shouldFailOnException");
+
+ Assertions.assertEquals(TestResult.Status.FAILED, result.getStatus());
+ Assertions.assertNotNull(result.getThrowable());
+ Assertions.assertEquals(IdentifiedTestException.class, result.getThrowable().getClass());
+ }
+
+ @Test
+ public void shouldReturnExceptionThrownInAfterClientWhenTestSucceeds() throws Exception {
+ Exception expectedException = new Exception("Expected");
+ TestScenarios.exceptionThrownInAfter = expectedException;
+
+ JUnitJupiterTestRunner runner = new JUnitJupiterTestRunner();
+ TestResult result = runner.execute(TestScenarios.class, "shouldSucceed");
+
+ Assertions.assertEquals(TestResult.Status.FAILED, result.getStatus());
+ Assertions.assertEquals(IdentifiedTestException.class, result.getThrowable().getClass());
+ }
+}
diff --git a/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitTestBaseClass.java b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitTestBaseClass.java
new file mode 100644
index 000000000..c68b78e12
--- /dev/null
+++ b/junit5/container/src/test/java/org/jboss/arquillian/junit5/container/JUnitTestBaseClass.java
@@ -0,0 +1,176 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2021 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @authors tag. All rights reserved.
+ * See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.container;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.arquillian.test.spi.LifecycleMethodExecutor;
+import org.jboss.arquillian.test.spi.TestMethodExecutor;
+import org.jboss.arquillian.test.spi.TestResult;
+import org.jboss.arquillian.test.spi.TestRunnerAdaptor;
+import org.jboss.arquillian.test.spi.TestRunnerAdaptorBuilder;
+import org.jboss.arquillian.test.spi.event.suite.TestLifecycleEvent;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.platform.engine.discovery.DiscoverySelectors;
+import org.junit.platform.launcher.Launcher;
+import org.junit.platform.launcher.LauncherDiscoveryRequest;
+import org.junit.platform.launcher.TestExecutionListener;
+import org.junit.platform.launcher.core.LauncherDiscoveryRequestBuilder;
+import org.junit.platform.launcher.core.LauncherFactory;
+import org.junit.platform.launcher.listeners.SummaryGeneratingListener;
+import org.junit.platform.launcher.listeners.TestExecutionSummary;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+
+/**
+ * JUnitTestBaseClass
+ *
+ * @author Aslak Knutsen
+ * @version $Revision: $
+ */
+public class JUnitTestBaseClass {
+ /*
+ * Setup / Clear the static callback info.
+ */
+ private static Map callbackCount = new HashMap<>();
+ private static Map callbackException = new HashMap<>();
+
+ static {
+ for (Cycle tmp : Cycle.values()) {
+ callbackCount.put(tmp, 0);
+ }
+ }
+
+ public static void wasCalled(Cycle cycle) throws Exception {
+ if (callbackCount.containsKey(cycle)) {
+ callbackCount.put(cycle, callbackCount.get(cycle) + 1);
+ } else {
+ throw new RuntimeException("Unknown callback: " + cycle);
+ }
+ if (callbackException.containsKey(cycle)) {
+ throw callbackException.get(cycle);
+ }
+ }
+
+ @AfterEach
+ public void clearCallbacks() {
+ callbackCount.clear();
+ for (Cycle tmp : Cycle.values()) {
+ callbackCount.put(tmp, 0);
+ }
+ callbackException.clear();
+ }
+
+ /*
+ * Internal Helpers
+ */
+ protected void executeAllLifeCycles(TestRunnerAdaptor adaptor) throws Exception {
+ doAnswer(new ExecuteLifecycle()).when(adaptor).beforeClass(any(Class.class), any(LifecycleMethodExecutor.class));
+ doAnswer(new ExecuteLifecycle()).when(adaptor).afterClass(any(Class.class), any(LifecycleMethodExecutor.class));
+ doAnswer(new ExecuteLifecycle()).when(adaptor).before(any(Object.class), any(Method.class),
+ any(LifecycleMethodExecutor.class));
+ doAnswer(new ExecuteLifecycle()).when(adaptor).after(any(Object.class), any(Method.class),
+ any(LifecycleMethodExecutor.class));
+ doAnswer(new TestExecuteLifecycle(TestResult.passed())).when(adaptor).test(any(TestMethodExecutor.class));
+ }
+
+ public void assertCycle(int count, Cycle... cycles) {
+ for (Cycle cycle : cycles) {
+ Assertions.assertEquals(count, (int) callbackCount.get(cycle), "Verify " + cycle + " called N times");
+ }
+ }
+
+ protected TestExecutionSummary run(TestRunnerAdaptor adaptor, Class>... classes) throws Exception {
+ return run(adaptor, null, classes);
+ }
+
+ protected TestExecutionSummary run(TestRunnerAdaptor adaptor, TestExecutionListener listener, Class>... classes)
+ throws Exception {
+ try {
+ setAdaptor(adaptor);
+
+ LauncherDiscoveryRequestBuilder builder = LauncherDiscoveryRequestBuilder.request();
+ for (Class> clazz : classes) {
+ builder = builder.selectors(DiscoverySelectors.selectClass(clazz));
+ }
+ LauncherDiscoveryRequest request = builder.build();
+ SummaryGeneratingListener summaryListener = new SummaryGeneratingListener();
+
+ Launcher launcher = LauncherFactory.create();
+ launcher.registerTestExecutionListeners(summaryListener);
+ if (listener != null) {
+ launcher.registerTestExecutionListeners(listener);
+ }
+ launcher.execute(request);
+
+ TestExecutionSummary summary = summaryListener.getSummary();
+ return summary;
+ } finally {
+ setAdaptor(null);
+ }
+ }
+
+ // force set the TestRunnerAdaptor to use
+ private void setAdaptor(TestRunnerAdaptor adaptor) throws Exception {
+ Method method = TestRunnerAdaptorBuilder.class.getMethod("set", TestRunnerAdaptor.class);
+ //method.setAccessible(true);
+ method.invoke(null, adaptor);
+ }
+
+ public enum Cycle {
+ BEFORE_CLASS_RULE, BEFORE_RULE, BEFORE_CLASS, BEFORE, TEST, AFTER, AFTER_CLASS, AFTER_RULE, AFTER_CLASS_RULE;
+ }
+
+ /*
+ * Mockito Answers for invoking the LifeCycle callbacks.
+ */
+ public static class ExecuteLifecycle implements Answer