From 601325f7104813132010830da8665da56f13936a Mon Sep 17 00:00:00 2001 From: James Nord Date: Mon, 30 Jan 2023 15:30:01 +0000 Subject: [PATCH] Add a JUnit 4 rule to skip tests if code makes use of GenericContainer or any other TestContainer based rule then those rules will mostl likely throw an exception when docker is not available. This leads people to add a BeforeClass annotation and test for docker, however this does not skip the tests, it skips the class. This has the unfortuante side effect that the report (from thinks like maven surefire and ant) that the test did not run when ingested into tools like Jenkins (as zero tests from the class either passed failed or skipped). it is also not obvious on the command line. by using a rule the individual tests will be marked as skipped so it becomes obvious that a test existed, but did not run for some reason (and the reason will be in the exception - that docker is not available) fixes #4586 / #343 --- .../RequireContainerSupportRule.java | 38 ++++++++++++++ .../RequireContainerSupportRuleTest.java | 51 +++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 core/src/main/java/org/testcontainers/RequireContainerSupportRule.java create mode 100644 core/src/test/java/org/testcontainers/RequireContainerSupportRuleTest.java diff --git a/core/src/main/java/org/testcontainers/RequireContainerSupportRule.java b/core/src/main/java/org/testcontainers/RequireContainerSupportRule.java new file mode 100644 index 00000000000..b0d74378cdd --- /dev/null +++ b/core/src/main/java/org/testcontainers/RequireContainerSupportRule.java @@ -0,0 +1,38 @@ +package org.testcontainers; + +import org.junit.AssumptionViolatedException; +import org.junit.Rule; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +/** + * Junit 4 {@code Rule} that will skip the test if the docker client is not available. + * This rule must have a lower {@link Rule#order} specified that any other {@code Rule} that + * uses docker in order to skip tests, otherwise the other rules would fail first causing build failures. + * e.g.

+    {@literal @}Rule(order = -10)
+    public RequireContainerSupportRule rcr = new RequireContainerSupportRule();
+ }
+ * 
+ */ +public class RequireContainerSupportRule implements TestRule { + + @Override + public Statement apply(Statement base, Description description) { + if (DockerClientFactory.instance().isDockerAvailable()) { + return base; + } + return new DockerNotAvailbleStatement(); + } + + private static class DockerNotAvailbleStatement extends Statement { + + @Override + public void evaluate() throws Throwable { + throw new AssumptionViolatedException( + "Docker support is not available and this test requires TestContainers which needs docker" + ); + } + } +} diff --git a/core/src/test/java/org/testcontainers/RequireContainerSupportRuleTest.java b/core/src/test/java/org/testcontainers/RequireContainerSupportRuleTest.java new file mode 100644 index 00000000000..41051441f7c --- /dev/null +++ b/core/src/test/java/org/testcontainers/RequireContainerSupportRuleTest.java @@ -0,0 +1,51 @@ +package org.testcontainers; + +import org.junit.AssumptionViolatedException; +import org.junit.Test; +import org.junit.runners.model.Statement; +import org.mockito.MockedStatic; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockStatic; +import static org.mockito.Mockito.when; + +public class RequireContainerSupportRuleTest { + + @Test + public void assumptionViolationisThrownWithoutDockerSpport() { + try (MockedStatic staticallyMockedFactory = mockStatic(DockerClientFactory.class)) { + DockerClientFactory mockedFactory = mock(DockerClientFactory.class); + when(mockedFactory.isDockerAvailable()).thenReturn(false); + staticallyMockedFactory.when(DockerClientFactory::instance).thenReturn(mockedFactory); + + RequireContainerSupportRule rcsr = new RequireContainerSupportRule(); + + assertThatThrownBy(() -> rcsr.apply(new EmptyStatement(), null).evaluate()) + .isInstanceOf(AssumptionViolatedException.class) + .hasMessage("Docker support is not available and this test requires TestContainers which needs docker"); + } + } + + @Test + public void assumptionViolationisNotThrownWithDockerSpport() throws Throwable { + try (MockedStatic staticallyMockedFactory = mockStatic(DockerClientFactory.class)) { + DockerClientFactory mockedFactory = mock(DockerClientFactory.class); + when(mockedFactory.isDockerAvailable()).thenReturn(true); + staticallyMockedFactory.when(DockerClientFactory::instance).thenReturn(mockedFactory); + + RequireContainerSupportRule rcsr = new RequireContainerSupportRule(); + + // any exception thrown will ripple out and fail the test + rcsr.apply(new EmptyStatement(), null).evaluate(); + } + } + + private static class EmptyStatement extends Statement { + + @Override + public void evaluate() throws Throwable { + // no op. + } + } +}