Skip to content

Commit

Permalink
Add a JUnit 4 rule to skip tests
Browse files Browse the repository at this point in the history
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 testcontainers#4586 / testcontainers#343
  • Loading branch information
jtnord committed Jan 30, 2023
1 parent 042fffd commit 601325f
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -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. <pre><code>
{@literal @}Rule(order = -10)
public RequireContainerSupportRule rcr = new RequireContainerSupportRule();
}
* </code></pre>
*/
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"
);
}
}
}
Original file line number Diff line number Diff line change
@@ -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<DockerClientFactory> 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<DockerClientFactory> 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.
}
}
}

0 comments on commit 601325f

Please sign in to comment.