From 63764cacd5cf70b9411d47e26d4dd9bb23caafe1 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sun, 21 Oct 2018 12:33:32 +0200 Subject: [PATCH 01/22] Update BrowserWebDriverContainer to honor existing no_proxy setting --- .../containers/BrowserWebDriverContainer.java | 6 +++- .../junit/BrowserWebDriverContainerTest.java | 34 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index 94374235cbb..f9ffb5c4d9b 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -156,7 +156,11 @@ protected void configure() { addExposedPorts(SELENIUM_PORT, VNC_PORT); addEnv("TZ", timeZone); - addEnv("no_proxy", "localhost"); + + if (!getEnvMap().containsKey("no_proxy")) { + addEnv("no_proxy", "localhost"); + } + setCommand("/opt/bin/entry_point.sh"); /* diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java new file mode 100644 index 00000000000..16ce8926f3c --- /dev/null +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -0,0 +1,34 @@ +package org.testcontainers.junit; + +import org.junit.Rule; +import org.junit.Test; +import org.openqa.selenium.remote.DesiredCapabilities; +import org.testcontainers.containers.BrowserWebDriverContainer; + +import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; + +public class BrowserWebDriverContainerTest { + + private static final String NO_PROXY_VALUE = "localhost,.noproxy-domain.com"; + + @Rule + public BrowserWebDriverContainer chromeWithNoProxySet = (BrowserWebDriverContainer) new BrowserWebDriverContainer() + .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withEnv("no_proxy", NO_PROXY_VALUE); + + @Rule + public BrowserWebDriverContainer chromeWithoutNoProxySet = new BrowserWebDriverContainer() + .withDesiredCapabilities(DesiredCapabilities.chrome()); + + @Test + public void honorPresetNoProxyEnvironment() { + Object no_proxy = chromeWithNoProxySet.getEnvMap().get("no_proxy"); + assertEquals("no_proxy should be preserved by the container rule", NO_PROXY_VALUE, no_proxy); + } + + @Test + public void provideDefaultNoProxyEnvironmentIfNotSet() { + Object no_proxy = chromeWithoutNoProxySet.getEnvMap().get("no_proxy"); + assertEquals("no_proxy should be set to default if not already present", "localhost", no_proxy); + } +} From 3281ac54f1b623b3220140843b2ca09296dc349d Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Mon, 22 Oct 2018 16:29:48 +0200 Subject: [PATCH 02/22] Update test to only start one container per test --- .../junit/BrowserWebDriverContainerTest.java | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 16ce8926f3c..5f6d22e79f9 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -1,6 +1,5 @@ package org.testcontainers.junit; -import org.junit.Rule; import org.junit.Test; import org.openqa.selenium.remote.DesiredCapabilities; import org.testcontainers.containers.BrowserWebDriverContainer; @@ -9,26 +8,38 @@ public class BrowserWebDriverContainerTest { - private static final String NO_PROXY_VALUE = "localhost,.noproxy-domain.com"; - - @Rule - public BrowserWebDriverContainer chromeWithNoProxySet = (BrowserWebDriverContainer) new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withEnv("no_proxy", NO_PROXY_VALUE); + private static final String NO_PROXY_KEY = "no_proxy"; - @Rule - public BrowserWebDriverContainer chromeWithoutNoProxySet = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()); + private static final String NO_PROXY_VALUE = "localhost,.noproxy-domain.com"; @Test public void honorPresetNoProxyEnvironment() { - Object no_proxy = chromeWithNoProxySet.getEnvMap().get("no_proxy"); - assertEquals("no_proxy should be preserved by the container rule", NO_PROXY_VALUE, no_proxy); + BrowserWebDriverContainer chromeWithNoProxySet = (BrowserWebDriverContainer) new BrowserWebDriverContainer() + .withDesiredCapabilities(DesiredCapabilities.chrome()) + .withEnv(NO_PROXY_KEY, NO_PROXY_VALUE); + + try { + chromeWithNoProxySet.start(); + + Object noProxy = chromeWithNoProxySet.getEnvMap().get(NO_PROXY_KEY); + assertEquals("no_proxy should be preserved by the container rule", NO_PROXY_VALUE, noProxy); + } finally { + chromeWithNoProxySet.stop(); + } } @Test public void provideDefaultNoProxyEnvironmentIfNotSet() { - Object no_proxy = chromeWithoutNoProxySet.getEnvMap().get("no_proxy"); - assertEquals("no_proxy should be set to default if not already present", "localhost", no_proxy); + BrowserWebDriverContainer chromeWithoutNoProxySet = new BrowserWebDriverContainer() + .withDesiredCapabilities(DesiredCapabilities.chrome()); + + try { + chromeWithoutNoProxySet.start(); + + Object noProxy = chromeWithoutNoProxySet.getEnvMap().get(NO_PROXY_KEY); + assertEquals("no_proxy should be set to default if not already present", "localhost", noProxy); + } finally { + chromeWithoutNoProxySet.stop(); + } } } From 631b9b3de6926778882d702798c8793ec9db7b97 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Mon, 22 Oct 2018 16:30:18 +0200 Subject: [PATCH 03/22] Use constant for no_proxy key --- .../containers/BrowserWebDriverContainer.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java index f9ffb5c4d9b..b92a4877cd2 100644 --- a/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java +++ b/modules/selenium/src/main/java/org/testcontainers/containers/BrowserWebDriverContainer.java @@ -48,6 +48,8 @@ public class BrowserWebDriverContainer Date: Sat, 2 Feb 2019 20:27:34 +0100 Subject: [PATCH 04/22] Cleanup test implementation (#929) --- .../junit/BrowserWebDriverContainerTest.java | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java index 5f6d22e79f9..036262a6bd1 100644 --- a/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java +++ b/modules/selenium/src/test/java/org/testcontainers/junit/BrowserWebDriverContainerTest.java @@ -1,7 +1,7 @@ package org.testcontainers.junit; import org.junit.Test; -import org.openqa.selenium.remote.DesiredCapabilities; +import org.openqa.selenium.chrome.ChromeOptions; import org.testcontainers.containers.BrowserWebDriverContainer; import static org.rnorth.visibleassertions.VisibleAssertions.assertEquals; @@ -14,32 +14,29 @@ public class BrowserWebDriverContainerTest { @Test public void honorPresetNoProxyEnvironment() { - BrowserWebDriverContainer chromeWithNoProxySet = (BrowserWebDriverContainer) new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()) - .withEnv(NO_PROXY_KEY, NO_PROXY_VALUE); - - try { + try ( + BrowserWebDriverContainer chromeWithNoProxySet = (BrowserWebDriverContainer) new BrowserWebDriverContainer() + .withCapabilities(new ChromeOptions()) + .withEnv(NO_PROXY_KEY, NO_PROXY_VALUE) + ) { chromeWithNoProxySet.start(); Object noProxy = chromeWithNoProxySet.getEnvMap().get(NO_PROXY_KEY); assertEquals("no_proxy should be preserved by the container rule", NO_PROXY_VALUE, noProxy); - } finally { - chromeWithNoProxySet.stop(); } } @Test public void provideDefaultNoProxyEnvironmentIfNotSet() { - BrowserWebDriverContainer chromeWithoutNoProxySet = new BrowserWebDriverContainer() - .withDesiredCapabilities(DesiredCapabilities.chrome()); + try ( + BrowserWebDriverContainer chromeWithoutNoProxySet = new BrowserWebDriverContainer() + .withCapabilities(new ChromeOptions()) - try { + ) { chromeWithoutNoProxySet.start(); Object noProxy = chromeWithoutNoProxySet.getEnvMap().get(NO_PROXY_KEY); assertEquals("no_proxy should be set to default if not already present", "localhost", noProxy); - } finally { - chromeWithoutNoProxySet.stop(); } } } From c91d344b497e7aee0a3d4118a5dc1ad39bde10bc Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Wed, 6 Feb 2019 15:26:54 +0100 Subject: [PATCH 05/22] Add signalling of TestLifecycleAware containers. Allow containers like WebBrowserContainers to initialize and/or finalize before/after tests. --- .../jupiter/TestcontainersExtension.java | 107 ++++++++++++++---- 1 file changed, 82 insertions(+), 25 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 7c5f8b205ef..0a8e2fc00ea 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -1,51 +1,119 @@ package org.testcontainers.junit.jupiter; import lombok.Getter; -import org.junit.jupiter.api.extension.*; +import org.jetbrains.annotations.NotNull; +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.ExtensionConfigurationException; +import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; +import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; import org.junit.platform.commons.support.AnnotationSupport; import org.junit.platform.commons.util.Preconditions; import org.junit.platform.commons.util.ReflectionUtils; import org.testcontainers.lifecycle.Startable; +import org.testcontainers.lifecycle.TestDescription; +import org.testcontainers.lifecycle.TestLifecycleAware; import java.lang.reflect.Field; import java.util.LinkedHashSet; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.function.Predicate; import java.util.stream.Stream; -class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, TestInstancePostProcessor { +import static java.util.stream.Collectors.toList; + +class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, TestInstancePostProcessor { private static final Namespace NAMESPACE = Namespace.create(TestcontainersExtension.class); private static final String TEST_INSTANCE = "testInstance"; + private static final String SHARED_LIFECYCLE_AWARE_CONTAINERS = "sharedLifecycleAwareContainers"; + private static final String LOCAL_LIFECYCLE_AWARE_CONTAINERS = "localLifecycleAwareContainers"; @Override public void postProcessTestInstance(final Object testInstance, final ExtensionContext context) { - ExtensionContext.Store store = context.getStore(NAMESPACE); + Store store = context.getStore(NAMESPACE); store.put(TEST_INSTANCE, testInstance); } @Override public void beforeAll(ExtensionContext context) { - Class testClass = context.getTestClass() - .orElseThrow(() -> new ExtensionConfigurationException("TestcontainersExtension is only supported for classes.")); + Class testClass = context.getTestClass().orElseThrow(() -> new ExtensionConfigurationException("TestcontainersExtension is only supported for classes.")); + Store store = context.getStore(NAMESPACE); + + List lifecycleAwareContainers = findSharedContainers(testClass) + .peek(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())) + .filter(this::isTestLifecycleAware) + .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) + .collect(toList()); - ExtensionContext.Store store = context.getStore(NAMESPACE); + store.put(SHARED_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers); + signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context)); + } - findSharedContainers(testClass) - .forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); + @Override + public void afterAll(ExtensionContext context) { + signalAfterTestToContainersFor(SHARED_LIFECYCLE_AWARE_CONTAINERS, context); } @Override public void beforeEach(final ExtensionContext context) { - collectParentTestInstances(context) - .parallelStream() + Store store = context.getStore(NAMESPACE); + + List lifecycleAwareContainers = collectParentTestInstances(context).parallelStream() .flatMap(this::findRestartContainers) - .forEach(adapter -> context.getStore(NAMESPACE) - .getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); + .peek(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())) + .filter(this::isTestLifecycleAware) + .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) + .collect(toList()); + + store.put(LOCAL_LIFECYCLE_AWARE_CONTAINERS, lifecycleAwareContainers); + signalBeforeTestToContainers(lifecycleAwareContainers, testDescriptionFrom(context)); + } + + @Override + public void afterEach(ExtensionContext context) { + signalAfterTestToContainersFor(LOCAL_LIFECYCLE_AWARE_CONTAINERS, context); + } + + private void signalBeforeTestToContainers(List lifecycleAwareContainers, TestDescription testDescription) { + lifecycleAwareContainers.forEach(container -> container.beforeTest(testDescription)); + } + + private void signalAfterTestToContainersFor(String storeKey, ExtensionContext context) { + List lifecycleAwareContainers = + (List) context.getStore(NAMESPACE).get(storeKey); + if (lifecycleAwareContainers != null) { + TestDescription description = testDescriptionFrom(context); + Optional throwable = context.getExecutionException(); + lifecycleAwareContainers.forEach(container -> container.afterTest(description, throwable)); + } + } + + @NotNull + private TestDescription testDescriptionFrom(ExtensionContext context) { + return new TestDescription() { + @Override + public String getTestId() { + return context.getUniqueId(); + } + + @Override + public String getFilesystemFriendlyName() { + return context.getDisplayName(); + } + }; + } + + private boolean isTestLifecycleAware(StoreAdapter adapter) { + return adapter.container instanceof TestLifecycleAware; } private Set collectParentTestInstances(final ExtensionContext context) { @@ -63,12 +131,7 @@ private Set collectParentTestInstances(final ExtensionContext context) { } private Stream findSharedContainers(Class testClass) { - return ReflectionUtils.findFields( - testClass, - isSharedContainer(), - ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) - .stream() - .map(f -> getContainerInstance(null, f)); + return ReflectionUtils.findFields(testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN).stream().map(f -> getContainerInstance(null, f)); } private Predicate isSharedContainer() { @@ -76,12 +139,7 @@ private Predicate isSharedContainer() { } private Stream findRestartContainers(Object testInstance) { - return ReflectionUtils.findFields( - testInstance.getClass(), - isRestartContainer(), - ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) - .stream() - .map(f -> getContainerInstance(testInstance, f)); + return ReflectionUtils.findFields(testInstance.getClass(), isRestartContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN).stream().map(f -> getContainerInstance(testInstance, f)); } private Predicate isRestartContainer() { @@ -93,7 +151,6 @@ private static Predicate isContainer() { boolean isAnnotatedWithContainer = AnnotationSupport.isAnnotated(field, Container.class); if (isAnnotatedWithContainer) { boolean isStartable = Startable.class.isAssignableFrom(field.getType()); - if (!isStartable) { throw new ExtensionConfigurationException("Annotation is only supported for Startable types"); } From a6998eb61f6fcdb62762284bec9ed0fb6ad9a129 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Wed, 20 Mar 2019 00:21:01 +0100 Subject: [PATCH 06/22] #1326 Add early draft to test signalling of TestLifecycleAware containers --- .../TestLifecycleAwareContainerMock.java | 35 +++++++++++++++++++ .../TestLifecycleAwareSignallingTest.java | 17 +++++++++ 2 files changed, 52 insertions(+) create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java new file mode 100644 index 00000000000..99b7177b486 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -0,0 +1,35 @@ +package org.testcontainers.junit.jupiter; + +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.lifecycle.TestDescription; +import org.testcontainers.lifecycle.TestLifecycleAware; + +import java.util.Optional; + +public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { + + private int numBeforeTestsCalls = 0; + private int numAfterTestsCalls = 0; + + public TestLifecycleAwareContainerMock() { + super("httpd:2.4-alpine"); + } + + @Override + public void beforeTest(TestDescription description) { + numBeforeTestsCalls++; + } + + @Override + public void afterTest(TestDescription description, Optional throwable) { + numAfterTestsCalls++; + } + + public int getNumBeforeTestsCalls() { + return numBeforeTestsCalls; + } + + public int getNumAfterTestsCalls() { + return numAfterTestsCalls; + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java new file mode 100644 index 00000000000..1f7aa41e539 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java @@ -0,0 +1,17 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.Test; + +import static org.junit.Assert.assertEquals; + +@Testcontainers +class TestLifecycleAwareSignallingTest { + + @Container + private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); + + @Test + void should_call_beforeTest_on_TestLifecycleAware_container() { + assertEquals(1, testContainer.getNumBeforeTestsCalls()); + } +} From e5a158d344675ecad04e721044c7bc16892d0a80 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Wed, 27 Mar 2019 09:07:31 +0100 Subject: [PATCH 07/22] Add test for post condition when signalling lifecycleaware containers This kind of test is a bit tricky since the post condition occurs after the original test has been finished. Also it's not nice to pass data between two tests. --- .../jupiter/TestLifecycleAwareSignallingTest.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java index 1f7aa41e539..d268750d726 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java @@ -1,17 +1,31 @@ package org.testcontainers.junit.jupiter; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; import static org.junit.Assert.assertEquals; @Testcontainers +@TestMethodOrder(OrderAnnotation.class) class TestLifecycleAwareSignallingTest { @Container private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); + static private TestLifecycleAwareContainerMock startedTestContainer; + @Test + @Order(1) void should_call_beforeTest_on_TestLifecycleAware_container() { assertEquals(1, testContainer.getNumBeforeTestsCalls()); + startedTestContainer = testContainer; + } + + @Test + @Order(2) + void should_call_afterTest_on_TestLifecycleAware_container() { + assertEquals(1, startedTestContainer.getNumAfterTestsCalls()); } } From ee1dae744008ca7e92b993d6290e1cfed845d525 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Tue, 23 Jul 2019 22:30:03 +0200 Subject: [PATCH 08/22] Update test for lifecycle aware containers to cover shared case (#1326) In order to check that the afterAll callback has signalled lifecycleaware containers correctly a second extension is used. The order of the extension annotation ensures that the assertion is run after the extension under test. --- .../TestLifecycleAwareSignallingTest.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java index 4af07f0a3b6..f0f47861e3f 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java @@ -4,9 +4,14 @@ import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; import static org.junit.Assert.assertEquals; +// The order of @ExtendsWith and @Testcontainers is crucial in order for the static case to be tested +@ExtendWith({TestLifecycleAwareSignallingTest.ExtensionSharedContainerTestExtension.class}) @Testcontainers @TestMethodOrder(OrderAnnotation.class) class TestLifecycleAwareSignallingTest { @@ -14,7 +19,10 @@ class TestLifecycleAwareSignallingTest { @Container private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); - static private TestLifecycleAwareContainerMock startedTestContainer; + @Container + private static final TestLifecycleAwareContainerMock SHARED_CONTAINER = new TestLifecycleAwareContainerMock(); + + private static TestLifecycleAwareContainerMock startedTestContainer; @Test @Order(1) @@ -27,5 +35,15 @@ void should_call_beforeTest_on_TestLifecycleAware_container() { @Order(2) void should_call_afterTest_on_TestLifecycleAware_container() { assertEquals(1, startedTestContainer.getNumAfterTestsCalls()); - } + } + + static class ExtensionSharedContainerTestExtension implements AfterAllCallback { + + @Override + public void afterAll(ExtensionContext context) { + assertEquals(1, SHARED_CONTAINER.getNumAfterTestsCalls()); + } + } } + + From 63c723da43f604fefe2e46c14fb4c5a2520e3e88 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Tue, 23 Jul 2019 22:45:42 +0200 Subject: [PATCH 09/22] Update test for lifecycle aware containers to cover shared case (#1326) To test the beforeAll() case the assertion has to be called from within the test class since it's called after all extensions. --- .../jupiter/TestLifecycleAwareSignallingTest.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java index f0f47861e3f..27b9bd61c8c 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java @@ -1,5 +1,6 @@ package org.testcontainers.junit.jupiter; +import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -11,7 +12,7 @@ import static org.junit.Assert.assertEquals; // The order of @ExtendsWith and @Testcontainers is crucial in order for the static case to be tested -@ExtendWith({TestLifecycleAwareSignallingTest.ExtensionSharedContainerTestExtension.class}) +@ExtendWith({TestLifecycleAwareSignallingTest.SharedContainerAfterAllTestExtension.class}) @Testcontainers @TestMethodOrder(OrderAnnotation.class) class TestLifecycleAwareSignallingTest { @@ -24,6 +25,11 @@ class TestLifecycleAwareSignallingTest { private static TestLifecycleAwareContainerMock startedTestContainer; + @BeforeAll + static void beforeAll() { + assertEquals(1, SHARED_CONTAINER.getNumBeforeTestsCalls()); + } + @Test @Order(1) void should_call_beforeTest_on_TestLifecycleAware_container() { @@ -37,10 +43,11 @@ void should_call_afterTest_on_TestLifecycleAware_container() { assertEquals(1, startedTestContainer.getNumAfterTestsCalls()); } - static class ExtensionSharedContainerTestExtension implements AfterAllCallback { + static class SharedContainerAfterAllTestExtension implements AfterAllCallback { @Override public void afterAll(ExtensionContext context) { + assertEquals(1, SHARED_CONTAINER.getNumBeforeTestsCalls()); assertEquals(1, SHARED_CONTAINER.getNumAfterTestsCalls()); } } From 93fb685c96d7cd528137121154b0ef82a2a8d9f5 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Fri, 1 Nov 2019 16:09:43 +0100 Subject: [PATCH 10/22] Fix formatting (#1326) --- .../junit/jupiter/TestcontainersExtension.java | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 23749687897..c231e630c65 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -164,7 +164,12 @@ private Set collectParentTestInstances(final ExtensionContext context) { } private Stream findSharedContainers(Class testClass) { - return ReflectionUtils.findFields(testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN).stream().map(f -> getContainerInstance(null, f)); + return ReflectionUtils.findFields( + testClass, + isSharedContainer(), + ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + .stream() + .map(f -> getContainerInstance(null, f)); } private Predicate isSharedContainer() { @@ -172,7 +177,12 @@ private Predicate isSharedContainer() { } private Stream findRestartContainers(Object testInstance) { - return ReflectionUtils.findFields(testInstance.getClass(), isRestartContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN).stream().map(f -> getContainerInstance(testInstance, f)); + return ReflectionUtils.findFields( + testInstance.getClass(), + isRestartContainer(), + ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) + .stream() + .map(f -> getContainerInstance(testInstance, f)); } private Predicate isRestartContainer() { From 13b526e20684fc836db62d57a8b101f2d609c911 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Fri, 1 Nov 2019 16:18:35 +0100 Subject: [PATCH 11/22] Use lighter container for testing (#1326) --- .../junit/jupiter/TestLifecycleAwareContainerMock.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java index 99b7177b486..a44fca4579c 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -11,8 +11,9 @@ public class TestLifecycleAwareContainerMock extends GenericContainer implements private int numBeforeTestsCalls = 0; private int numAfterTestsCalls = 0; - public TestLifecycleAwareContainerMock() { - super("httpd:2.4-alpine"); + TestLifecycleAwareContainerMock() { + super("alpine:3.2"); + setCommand("top"); } @Override @@ -25,11 +26,11 @@ public void afterTest(TestDescription description, Optional throwable numAfterTestsCalls++; } - public int getNumBeforeTestsCalls() { + int getNumBeforeTestsCalls() { return numBeforeTestsCalls; } - public int getNumAfterTestsCalls() { + int getNumAfterTestsCalls() { return numAfterTestsCalls; } } From eb67ee2237a0e5ec7134d2a18a9cffcd1ab7a12e Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Fri, 1 Nov 2019 16:32:00 +0100 Subject: [PATCH 12/22] Separate store and collect of shared lifecycle-aware-containers (#1326) --- .../junit/jupiter/TestcontainersExtension.java | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index c231e630c65..3e49f27375f 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -43,10 +43,14 @@ public void postProcessTestInstance(final Object testInstance, final ExtensionCo public void beforeAll(ExtensionContext context) { Class testClass = context.getTestClass() .orElseThrow(() -> new ExtensionConfigurationException("TestcontainersExtension is only supported for classes.")); + Store store = context.getStore(NAMESPACE); + List sharedContainersStoreAdapters = findSharedContainers(testClass); - List lifecycleAwareContainers = findSharedContainers(testClass) - .peek(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())) + sharedContainersStoreAdapters.forEach(adapter -> store.getOrComputeIfAbsent(adapter.getKey(), k -> adapter.start())); + + List lifecycleAwareContainers = sharedContainersStoreAdapters + .stream() .filter(this::isTestLifecycleAware) .map(lifecycleAwareAdapter -> (TestLifecycleAware) lifecycleAwareAdapter.container) .collect(toList()); @@ -163,13 +167,14 @@ private Set collectParentTestInstances(final ExtensionContext context) { return testInstances; } - private Stream findSharedContainers(Class testClass) { + private List findSharedContainers(Class testClass) { return ReflectionUtils.findFields( testClass, isSharedContainer(), ReflectionUtils.HierarchyTraversalMode.TOP_DOWN) .stream() - .map(f -> getContainerInstance(null, f)); + .map(f -> getContainerInstance(null, f)) + .collect(toList()); } private Predicate isSharedContainer() { From 37014cc0ceca2d96935b1f6c677ee520c3c92e1e Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Fri, 1 Nov 2019 17:15:55 +0100 Subject: [PATCH 13/22] Add tests for ordering and capturing test exceptions (#1326) --- .../TestLifecycleAwareContainerMock.java | 32 +++++++++++++++++ .../TestLifecycleAwareSignallingTest.java | 36 +++++++++++++++++-- 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java index a44fca4579c..7c8c8b1c6ed 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -6,11 +6,19 @@ import java.util.Optional; +import static java.lang.System.currentTimeMillis; +import static java.lang.Thread.sleep; + public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { private int numBeforeTestsCalls = 0; private int numAfterTestsCalls = 0; + private long beforeTestCalledAtMillis = 0; + private long afterTestCalledAtMillis = 0; + + private Throwable capturedThrowable; + TestLifecycleAwareContainerMock() { super("alpine:3.2"); setCommand("top"); @@ -19,11 +27,23 @@ public class TestLifecycleAwareContainerMock extends GenericContainer implements @Override public void beforeTest(TestDescription description) { numBeforeTestsCalls++; + beforeTestCalledAtMillis = currentTimeMillis(); + ensureMillisHavePassed(); } @Override public void afterTest(TestDescription description, Optional throwable) { numAfterTestsCalls++; + afterTestCalledAtMillis = currentTimeMillis(); + throwable.ifPresent(capturedThrowable -> this.capturedThrowable = capturedThrowable); + } + + private void ensureMillisHavePassed() { + try { + sleep(5); + } catch (InterruptedException e) { + throw new RuntimeException("Unable to sleep for 10ms"); + } } int getNumBeforeTestsCalls() { @@ -33,4 +53,16 @@ int getNumBeforeTestsCalls() { int getNumAfterTestsCalls() { return numAfterTestsCalls; } + + long getBeforeTestCalledAtMillis() { + return beforeTestCalledAtMillis; + } + + long getAfterTestCalledAtMillis() { + return afterTestCalledAtMillis; + } + + Throwable getCapturedThrowable() { + return capturedThrowable; + } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java index 27b9bd61c8c..73c230e9cc2 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java @@ -1,6 +1,10 @@ package org.testcontainers.junit.jupiter; +import org.junit.Assume; +import org.junit.AssumptionViolatedException; +import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; import org.junit.jupiter.api.Order; import org.junit.jupiter.api.Test; @@ -10,6 +14,9 @@ import org.junit.jupiter.api.extension.ExtensionContext; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; // The order of @ExtendsWith and @Testcontainers is crucial in order for the static case to be tested @ExtendWith({TestLifecycleAwareSignallingTest.SharedContainerAfterAllTestExtension.class}) @@ -43,14 +50,39 @@ void should_call_afterTest_on_TestLifecycleAware_container() { assertEquals(1, startedTestContainer.getNumAfterTestsCalls()); } + @Test + @Order(3) + void should_call_beforeTest_before_afterTest() { + long beforeTestCalledAtMillis = startedTestContainer.getBeforeTestCalledAtMillis(); + long afterTestCalledAtMillis = startedTestContainer.getAfterTestCalledAtMillis(); + assertTrue(beforeTestCalledAtMillis < afterTestCalledAtMillis); + } + + @Test + @Order(4) + void failing_test_should_pass_throwable_to_testContainer() { + startedTestContainer = testContainer; + assumeTrue(false); + } + + @Test + @Order(5) + void should_have_captured_thrownException() { + Throwable capturedThrowable = startedTestContainer.getCapturedThrowable(); + assertNotNull(capturedThrowable); + assertTrue(capturedThrowable instanceof AssumptionViolatedException); + assertEquals("got: , expected: is ", capturedThrowable.getMessage()); + } + static class SharedContainerAfterAllTestExtension implements AfterAllCallback { @Override public void afterAll(ExtensionContext context) { assertEquals(1, SHARED_CONTAINER.getNumBeforeTestsCalls()); assertEquals(1, SHARED_CONTAINER.getNumAfterTestsCalls()); + long beforeTestCalledAtMillis = SHARED_CONTAINER.getBeforeTestCalledAtMillis(); + long afterTestCalledAtMillis = SHARED_CONTAINER.getAfterTestCalledAtMillis(); + assertTrue(beforeTestCalledAtMillis < afterTestCalledAtMillis); } } } - - From 601327337b99ba13ab07db720021d69cd24ca6fd Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Mon, 23 Dec 2019 11:08:21 +0100 Subject: [PATCH 14/22] Make lifecycle tests independent of timing (#1326) Calls to the lifecycle methods are now recorded in an ordered list that is then used to test the correct number and order of calls. This makes the test independent of timing. Unfortunately it's still required to execute tests in a deterministic order. For a better separation of test concerns tests for the lifecycle methods and exception capturing have been moved into separate test classes. --- .../TestLifecycleAwareContainerMock.java | 43 ++------- ...tLifecycleAwareExceptionCapturingTest.java | 37 ++++++++ .../jupiter/TestLifecycleAwareMethodTest.java | 58 ++++++++++++ .../TestLifecycleAwareSignallingTest.java | 88 ------------------- 4 files changed, 104 insertions(+), 122 deletions(-) create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java delete mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java index 7c8c8b1c6ed..fc7370fbea5 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -4,18 +4,16 @@ import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; +import java.util.ArrayList; +import java.util.List; import java.util.Optional; -import static java.lang.System.currentTimeMillis; -import static java.lang.Thread.sleep; - public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { - private int numBeforeTestsCalls = 0; - private int numAfterTestsCalls = 0; + static final String BEFORE_TEST = "beforeTest"; + static final String AFTER_TEST = "afterTest"; - private long beforeTestCalledAtMillis = 0; - private long afterTestCalledAtMillis = 0; + private List lifecycleMethodCalls = new ArrayList<>(); private Throwable capturedThrowable; @@ -26,40 +24,17 @@ public class TestLifecycleAwareContainerMock extends GenericContainer implements @Override public void beforeTest(TestDescription description) { - numBeforeTestsCalls++; - beforeTestCalledAtMillis = currentTimeMillis(); - ensureMillisHavePassed(); + lifecycleMethodCalls.add(BEFORE_TEST); } @Override public void afterTest(TestDescription description, Optional throwable) { - numAfterTestsCalls++; - afterTestCalledAtMillis = currentTimeMillis(); + lifecycleMethodCalls.add(AFTER_TEST); throwable.ifPresent(capturedThrowable -> this.capturedThrowable = capturedThrowable); } - private void ensureMillisHavePassed() { - try { - sleep(5); - } catch (InterruptedException e) { - throw new RuntimeException("Unable to sleep for 10ms"); - } - } - - int getNumBeforeTestsCalls() { - return numBeforeTestsCalls; - } - - int getNumAfterTestsCalls() { - return numAfterTestsCalls; - } - - long getBeforeTestCalledAtMillis() { - return beforeTestCalledAtMillis; - } - - long getAfterTestCalledAtMillis() { - return afterTestCalledAtMillis; + List getLifecycleMethodCalls() { + return lifecycleMethodCalls; } Throwable getCapturedThrowable() { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java new file mode 100644 index 00000000000..85d837a7880 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareExceptionCapturingTest.java @@ -0,0 +1,37 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.AssumptionViolatedException; +import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +// The order of @ExtendsWith and @Testcontainers is crucial in order for the tests +@Testcontainers +@TestMethodOrder(OrderAnnotation.class) +class TestLifecycleAwareExceptionCapturingTest { + @Container + private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); + + private static TestLifecycleAwareContainerMock startedTestContainer; + + @Test + @Order(1) + void failing_test_should_pass_throwable_to_testContainer() { + startedTestContainer = testContainer; + // Force an exception that is captured by the test container without failing the test itself + assumeTrue(false); + } + + @Test + @Order(2) + void should_have_captured_thrownException() { + Throwable capturedThrowable = startedTestContainer.getCapturedThrowable(); + assertTrue(capturedThrowable instanceof AssumptionViolatedException); + assertEquals("got: , expected: is ", capturedThrowable.getMessage()); + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java new file mode 100644 index 00000000000..ca9e6604354 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -0,0 +1,58 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.MethodOrderer; +import org.junit.jupiter.api.Order; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestMethodOrder; +import org.junit.jupiter.api.extension.AfterAllCallback; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.util.Collections; + +import static java.util.Arrays.asList; +import static java.util.Collections.singletonList; +import static org.junit.Assert.assertEquals; +import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.AFTER_TEST; +import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.BEFORE_TEST; + +// The order of @ExtendsWith and @Testcontainers is crucial for the tests +@ExtendWith({TestLifecycleAwareMethodTest.SharedContainerAfterAllTestExtension.class}) +@Testcontainers +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +class TestLifecycleAwareMethodTest { + @Container + private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); + + @Container + private static final TestLifecycleAwareContainerMock SHARED_CONTAINER = new TestLifecycleAwareContainerMock(); + + private static TestLifecycleAwareContainerMock startedTestContainer; + + @BeforeAll + static void beforeAll() { + assertEquals(singletonList(BEFORE_TEST), SHARED_CONTAINER.getLifecycleMethodCalls()); + } + + @Test + @Order(1) + void should_prepare_before_and_after_test() { + // we can only test for a call to afterTest() after this test has been finished. + startedTestContainer = testContainer; + } + + @Test + @Order(2) + void should_call_beforeTest_first_afterTest_later() { + assertEquals(asList(BEFORE_TEST, AFTER_TEST), startedTestContainer.getLifecycleMethodCalls()); + } + + static class SharedContainerAfterAllTestExtension implements AfterAllCallback { + // Unfortunately it's not possible to write a @Test that is run after all tests + @Override + public void afterAll(ExtensionContext context) { + assertEquals(asList(BEFORE_TEST, AFTER_TEST), SHARED_CONTAINER.getLifecycleMethodCalls()); + } + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java deleted file mode 100644 index 73c230e9cc2..00000000000 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareSignallingTest.java +++ /dev/null @@ -1,88 +0,0 @@ -package org.testcontainers.junit.jupiter; - -import org.junit.Assume; -import org.junit.AssumptionViolatedException; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.MethodOrderer.OrderAnnotation; -import org.junit.jupiter.api.Order; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestMethodOrder; -import org.junit.jupiter.api.extension.AfterAllCallback; -import org.junit.jupiter.api.extension.ExtendWith; -import org.junit.jupiter.api.extension.ExtensionContext; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; -import static org.junit.Assume.assumeTrue; - -// The order of @ExtendsWith and @Testcontainers is crucial in order for the static case to be tested -@ExtendWith({TestLifecycleAwareSignallingTest.SharedContainerAfterAllTestExtension.class}) -@Testcontainers -@TestMethodOrder(OrderAnnotation.class) -class TestLifecycleAwareSignallingTest { - - @Container - private final TestLifecycleAwareContainerMock testContainer = new TestLifecycleAwareContainerMock(); - - @Container - private static final TestLifecycleAwareContainerMock SHARED_CONTAINER = new TestLifecycleAwareContainerMock(); - - private static TestLifecycleAwareContainerMock startedTestContainer; - - @BeforeAll - static void beforeAll() { - assertEquals(1, SHARED_CONTAINER.getNumBeforeTestsCalls()); - } - - @Test - @Order(1) - void should_call_beforeTest_on_TestLifecycleAware_container() { - assertEquals(1, testContainer.getNumBeforeTestsCalls()); - startedTestContainer = testContainer; - } - - @Test - @Order(2) - void should_call_afterTest_on_TestLifecycleAware_container() { - assertEquals(1, startedTestContainer.getNumAfterTestsCalls()); - } - - @Test - @Order(3) - void should_call_beforeTest_before_afterTest() { - long beforeTestCalledAtMillis = startedTestContainer.getBeforeTestCalledAtMillis(); - long afterTestCalledAtMillis = startedTestContainer.getAfterTestCalledAtMillis(); - assertTrue(beforeTestCalledAtMillis < afterTestCalledAtMillis); - } - - @Test - @Order(4) - void failing_test_should_pass_throwable_to_testContainer() { - startedTestContainer = testContainer; - assumeTrue(false); - } - - @Test - @Order(5) - void should_have_captured_thrownException() { - Throwable capturedThrowable = startedTestContainer.getCapturedThrowable(); - assertNotNull(capturedThrowable); - assertTrue(capturedThrowable instanceof AssumptionViolatedException); - assertEquals("got: , expected: is ", capturedThrowable.getMessage()); - } - - static class SharedContainerAfterAllTestExtension implements AfterAllCallback { - - @Override - public void afterAll(ExtensionContext context) { - assertEquals(1, SHARED_CONTAINER.getNumBeforeTestsCalls()); - assertEquals(1, SHARED_CONTAINER.getNumAfterTestsCalls()); - long beforeTestCalledAtMillis = SHARED_CONTAINER.getBeforeTestCalledAtMillis(); - long afterTestCalledAtMillis = SHARED_CONTAINER.getAfterTestCalledAtMillis(); - assertTrue(beforeTestCalledAtMillis < afterTestCalledAtMillis); - } - } -} From 5de31c8a80dc4f4b22cbd2d74c01e82673a6a615 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Mon, 23 Dec 2019 14:21:36 +0100 Subject: [PATCH 15/22] Make mock now implements Startable (#1326) There is no need to start a container since only the TestLifecycleAware is important. --- .../TestLifecycleAwareContainerMock.java | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java index fc7370fbea5..f3b47a60ed8 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -1,6 +1,6 @@ package org.testcontainers.junit.jupiter; -import org.testcontainers.containers.GenericContainer; +import org.testcontainers.lifecycle.Startable; import org.testcontainers.lifecycle.TestDescription; import org.testcontainers.lifecycle.TestLifecycleAware; @@ -8,20 +8,15 @@ import java.util.List; import java.util.Optional; -public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { +public class TestLifecycleAwareContainerMock implements Startable, TestLifecycleAware { static final String BEFORE_TEST = "beforeTest"; static final String AFTER_TEST = "afterTest"; - private List lifecycleMethodCalls = new ArrayList<>(); + private final List lifecycleMethodCalls = new ArrayList<>(); private Throwable capturedThrowable; - TestLifecycleAwareContainerMock() { - super("alpine:3.2"); - setCommand("top"); - } - @Override public void beforeTest(TestDescription description) { lifecycleMethodCalls.add(BEFORE_TEST); @@ -40,4 +35,14 @@ List getLifecycleMethodCalls() { Throwable getCapturedThrowable() { return capturedThrowable; } + + @Override + public void start() { + + } + + @Override + public void stop() { + + } } From 9abd4e6a7e214fbb5e2b9b8e8c8b289566a916f1 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sun, 19 Jan 2020 14:57:42 +0100 Subject: [PATCH 16/22] Add AssertJ dependency (#1326) We want to use AssertJ for some tests. --- modules/junit-jupiter/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 18cc5ddfe96..06047b8c7ff 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,6 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.2.4') { exclude(module: 'hamcrest-core') } + testCompile 'org.assertj:assertj-core:3.14.0' testRuntime 'org.postgresql:postgresql:42.2.9' testRuntime 'mysql:mysql-connector-java:8.0.18' From 43ec440b5116c958e17337938d25687bb9e24f60 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sun, 19 Jan 2020 14:58:10 +0100 Subject: [PATCH 17/22] Migrate assertions of TestLifecycleAwareMethodTest to AssertJ (#1326) --- .../junit/jupiter/TestLifecycleAwareMethodTest.java | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java index ca9e6604354..a891699a1ca 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -9,11 +9,7 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.api.extension.ExtensionContext; -import java.util.Collections; - -import static java.util.Arrays.asList; -import static java.util.Collections.singletonList; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.AFTER_TEST; import static org.testcontainers.junit.jupiter.TestLifecycleAwareContainerMock.BEFORE_TEST; @@ -32,7 +28,8 @@ class TestLifecycleAwareMethodTest { @BeforeAll static void beforeAll() { - assertEquals(singletonList(BEFORE_TEST), SHARED_CONTAINER.getLifecycleMethodCalls()); + assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()).containsExactly(BEFORE_TEST); + } @Test @@ -45,14 +42,14 @@ void should_prepare_before_and_after_test() { @Test @Order(2) void should_call_beforeTest_first_afterTest_later() { - assertEquals(asList(BEFORE_TEST, AFTER_TEST), startedTestContainer.getLifecycleMethodCalls()); + assertThat(startedTestContainer.getLifecycleMethodCalls()).containsExactly(BEFORE_TEST, AFTER_TEST); } static class SharedContainerAfterAllTestExtension implements AfterAllCallback { // Unfortunately it's not possible to write a @Test that is run after all tests @Override public void afterAll(ExtensionContext context) { - assertEquals(asList(BEFORE_TEST, AFTER_TEST), SHARED_CONTAINER.getLifecycleMethodCalls()); + assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()).containsExactly(BEFORE_TEST, AFTER_TEST); } } } From 856da3f885e049e2963cfcf574a355b95f8aa3d2 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sun, 19 Jan 2020 16:10:17 +0100 Subject: [PATCH 18/22] Update generation of filesystem friendly description (#1326) --- .../jupiter/TestcontainersExtension.java | 20 +++++++++---------- .../TestcontainersTestDescription.java | 20 +++++++++++++++++++ .../TestLifecycleAwareContainerMock.java | 6 ++++++ .../jupiter/TestLifecycleAwareMethodTest.java | 18 ++++++++++++++--- 4 files changed, 51 insertions(+), 13 deletions(-) create mode 100644 modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index 3e49f27375f..d47f572b68a 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -16,6 +16,7 @@ import org.testcontainers.lifecycle.TestLifecycleAware; import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.LinkedHashSet; import java.util.List; import java.util.Optional; @@ -99,17 +100,16 @@ private void signalAfterTestToContainersFor(String storeKey, ExtensionContext co } private TestDescription testDescriptionFrom(ExtensionContext context) { - return new TestDescription() { - @Override - public String getTestId() { - return context.getUniqueId(); - } + return new TestcontainersTestDescription( + context.getUniqueId(), + filesystemFriendlyNameOf(context) + ); + } - @Override - public String getFilesystemFriendlyName() { - return context.getDisplayName(); - } - }; + private String filesystemFriendlyNameOf(ExtensionContext context) { + return context.getRequiredTestClass().getName() + + "-" + + context.getTestMethod().map(Method::getName).orElse("static"); } private boolean isTestLifecycleAware(StoreAdapter adapter) { diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java new file mode 100644 index 00000000000..d5e38b20582 --- /dev/null +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java @@ -0,0 +1,20 @@ +package org.testcontainers.junit.jupiter; + +import lombok.RequiredArgsConstructor; +import org.testcontainers.lifecycle.TestDescription; + +@RequiredArgsConstructor +public class TestcontainersTestDescription implements TestDescription { + private final String testId; + private final String filesystemFriendlyName; + + @Override + public String getTestId() { + return testId; + } + + @Override + public String getFilesystemFriendlyName() { + return filesystemFriendlyName; + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java index f3b47a60ed8..38751c8789a 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareContainerMock.java @@ -14,12 +14,14 @@ public class TestLifecycleAwareContainerMock implements Startable, TestLifecycle static final String AFTER_TEST = "afterTest"; private final List lifecycleMethodCalls = new ArrayList<>(); + private final List lifecycleFilesystemFriendlyNames = new ArrayList<>(); private Throwable capturedThrowable; @Override public void beforeTest(TestDescription description) { lifecycleMethodCalls.add(BEFORE_TEST); + lifecycleFilesystemFriendlyNames.add(description.getFilesystemFriendlyName()); } @Override @@ -36,6 +38,10 @@ Throwable getCapturedThrowable() { return capturedThrowable; } + public List getLifecycleFilesystemFriendlyNames() { + return lifecycleFilesystemFriendlyNames; + } + @Override public void start() { diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java index a891699a1ca..cdf61282834 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -41,15 +41,27 @@ void should_prepare_before_and_after_test() { @Test @Order(2) - void should_call_beforeTest_first_afterTest_later() { - assertThat(startedTestContainer.getLifecycleMethodCalls()).containsExactly(BEFORE_TEST, AFTER_TEST); + void should_call_beforeTest_first_afterTest_later_with_filesystem_friendly_name() { + assertThat(startedTestContainer.getLifecycleMethodCalls()) + .containsExactly(BEFORE_TEST, AFTER_TEST); + // Usually this would be a separate test, but due to the complex setup this is done here as well + assertThat(startedTestContainer.getLifecycleFilesystemFriendlyNames()) + .containsExactly( + "org.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest-should_prepare_before_and_after_test" + ); } static class SharedContainerAfterAllTestExtension implements AfterAllCallback { // Unfortunately it's not possible to write a @Test that is run after all tests @Override public void afterAll(ExtensionContext context) { - assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()).containsExactly(BEFORE_TEST, AFTER_TEST); + assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()) + .containsExactly(BEFORE_TEST, AFTER_TEST); + // Usually this would be a separate test, but due to the complex setup this is done here as well + assertThat(SHARED_CONTAINER.getLifecycleFilesystemFriendlyNames()) + .containsExactly( + "org.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest-static" + ); } } } From c93cebd3dbc32ee106df2e251f6dbb631d5e8c1d Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Thu, 23 Jan 2020 19:00:19 +0100 Subject: [PATCH 19/22] Separated tests for filesystem friendly filename (#1326) --- .../jupiter/TestLifecycleAwareMethodTest.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java index cdf61282834..bc34f2136bd 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -44,24 +44,30 @@ void should_prepare_before_and_after_test() { void should_call_beforeTest_first_afterTest_later_with_filesystem_friendly_name() { assertThat(startedTestContainer.getLifecycleMethodCalls()) .containsExactly(BEFORE_TEST, AFTER_TEST); - // Usually this would be a separate test, but due to the complex setup this is done here as well + } + + @Test + void should_have_a_filesystem_friendly_name_container_has_started() { assertThat(startedTestContainer.getLifecycleFilesystemFriendlyNames()) .containsExactly( "org.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest-should_prepare_before_and_after_test" ); } + @Test + void static_container_should_have_a_filesystem_friendly_name_after_container_has_started() { + assertThat(SHARED_CONTAINER.getLifecycleFilesystemFriendlyNames()) + .containsExactly( + "org.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest-static" + ); + } + static class SharedContainerAfterAllTestExtension implements AfterAllCallback { // Unfortunately it's not possible to write a @Test that is run after all tests @Override public void afterAll(ExtensionContext context) { assertThat(SHARED_CONTAINER.getLifecycleMethodCalls()) .containsExactly(BEFORE_TEST, AFTER_TEST); - // Usually this would be a separate test, but due to the complex setup this is done here as well - assertThat(SHARED_CONTAINER.getLifecycleFilesystemFriendlyNames()) - .containsExactly( - "org.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest-static" - ); } } } From 2fbdf8500ed49cf67fb693dbebe34c4555621c8d Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sat, 29 Feb 2020 10:43:47 +0100 Subject: [PATCH 20/22] Use lombok to improve readability (#1326) --- .../TestcontainersTestDescription.java | 20 +++++-------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java index d5e38b20582..568d228e08c 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java @@ -1,20 +1,10 @@ package org.testcontainers.junit.jupiter; -import lombok.RequiredArgsConstructor; +import lombok.Value; import org.testcontainers.lifecycle.TestDescription; -@RequiredArgsConstructor -public class TestcontainersTestDescription implements TestDescription { - private final String testId; - private final String filesystemFriendlyName; - - @Override - public String getTestId() { - return testId; - } - - @Override - public String getFilesystemFriendlyName() { - return filesystemFriendlyName; - } +@Value +class TestcontainersTestDescription implements TestDescription { + String testId; + String filesystemFriendlyName; } From 6f4f83a9878bef39bc60e86f623ed840e4645f15 Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sat, 29 Feb 2020 11:54:14 +0100 Subject: [PATCH 21/22] Generate filesystem friendly name from display name (#1326) Generating a filesystem friendly name in a Junit Jupiter test is a bit tricky. Since tests can be generated dynamically class and/or test method may not be available. The display name provided by the ExtensionContext on the other hand may use characters that are not filesystem safe. This approach removes all characters from the display name that are not in a restricted set of allowed characters. However this may lead to name clashes if two tests have a display name that only differs in characters that are removed from the display name. --- modules/junit-jupiter/build.gradle | 1 + .../FilesystemFriendlyNameGenerator.java | 23 +++++++++++ ...java => TestcontainerTestDescription.java} | 0 .../jupiter/TestcontainersExtension.java | 16 ++++---- .../FilesystemFriendlyNameGeneratorTest.java | 40 +++++++++++++++++++ .../jupiter/TestLifecycleAwareMethodTest.java | 4 +- 6 files changed, 75 insertions(+), 9 deletions(-) create mode 100644 modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java rename modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/{TestcontainersTestDescription.java => TestcontainerTestDescription.java} (100%) create mode 100644 modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java diff --git a/modules/junit-jupiter/build.gradle b/modules/junit-jupiter/build.gradle index 76f7e30e9ba..95e2869a0b0 100644 --- a/modules/junit-jupiter/build.gradle +++ b/modules/junit-jupiter/build.gradle @@ -12,6 +12,7 @@ dependencies { testCompile ('org.mockito:mockito-core:3.2.4') { exclude(module: 'hamcrest-core') } + testCompile 'org.junit.jupiter:junit-jupiter-params:5.6.0' testCompile 'org.assertj:assertj-core:3.14.0' testRuntime 'org.postgresql:postgresql:42.2.10' diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java new file mode 100644 index 00000000000..074be22c912 --- /dev/null +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java @@ -0,0 +1,23 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.extension.ExtensionContext; + +import static org.junit.platform.commons.util.StringUtils.isBlank; + +class FilesystemFriendlyNameGenerator { + private static final String UNKNOWN_NAME = "unknown"; + private static final String ALLOWED_CHARACTERS_REGEX = "[^\\w -]"; + + static String filesystemFriendlyNameOf(ExtensionContext context) { + String displayName = context.getDisplayName(); + if (isBlank(displayName)) { + return UNKNOWN_NAME; + } + + return replaceIllegalCharacters(displayName.trim()); + } + + private static String replaceIllegalCharacters(String source) { + return source.replaceAll(ALLOWED_CHARACTERS_REGEX, ""); + } +} diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java similarity index 100% rename from modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersTestDescription.java rename to modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainerTestDescription.java diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java index d47f572b68a..1ed5e954e44 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/TestcontainersExtension.java @@ -1,7 +1,14 @@ package org.testcontainers.junit.jupiter; import lombok.Getter; -import org.junit.jupiter.api.extension.*; +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.ConditionEvaluationResult; +import org.junit.jupiter.api.extension.ExecutionCondition; +import org.junit.jupiter.api.extension.ExtensionConfigurationException; +import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.api.extension.ExtensionContext.Namespace; import org.junit.jupiter.api.extension.ExtensionContext.Store; import org.junit.jupiter.api.extension.ExtensionContext.Store.CloseableResource; @@ -25,6 +32,7 @@ import java.util.stream.Stream; import static java.util.stream.Collectors.toList; +import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; class TestcontainersExtension implements BeforeEachCallback, BeforeAllCallback, AfterEachCallback, AfterAllCallback, ExecutionCondition, TestInstancePostProcessor { @@ -106,12 +114,6 @@ private TestDescription testDescriptionFrom(ExtensionContext context) { ); } - private String filesystemFriendlyNameOf(ExtensionContext context) { - return context.getRequiredTestClass().getName() - + "-" - + context.getTestMethod().map(Method::getName).orElse("static"); - } - private boolean isTestLifecycleAware(StoreAdapter adapter) { return adapter.container instanceof TestLifecycleAware; } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java new file mode 100644 index 00000000000..a1e6326a966 --- /dev/null +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java @@ -0,0 +1,40 @@ +package org.testcontainers.junit.jupiter; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.testcontainers.junit.jupiter.FilesystemFriendlyNameGenerator.filesystemFriendlyNameOf; + +class FilesystemFriendlyNameGeneratorTest { + + @ParameterizedTest + @MethodSource("provideDisplayNamesAndFilesystemFriendlyNames") + void should_generate_filesystem_friendly_name(String displayName, String expectedName) { + ExtensionContext context = mock(ExtensionContext.class); + doReturn(displayName) + .when(context).getDisplayName(); + + String filesystemFriendlyName = filesystemFriendlyNameOf(context); + + assertThat(filesystemFriendlyName).isEqualTo(expectedName); + } + + private static Stream provideDisplayNamesAndFilesystemFriendlyNames() { + return Stream.of( + Arguments.of("", "unknown"), + Arguments.of(" ", "unknown"), + Arguments.of("not blank", "not blank"), + Arguments.of("abc ABC 1234567890", "abc ABC 1234567890"), + Arguments.of("no_umlauts_äöüÄÖÜéáíó", "no_umlauts_"), + Arguments.of("no\ttabs", "notabs"), + Arguments.of("no_special_[]{}/?<>!@#$%^&*()+=\\|'\";:`~", "no_special_") + ); + } +} diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java index bc34f2136bd..f3b4928a96e 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -50,7 +50,7 @@ void should_call_beforeTest_first_afterTest_later_with_filesystem_friendly_name( void should_have_a_filesystem_friendly_name_container_has_started() { assertThat(startedTestContainer.getLifecycleFilesystemFriendlyNames()) .containsExactly( - "org.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest-should_prepare_before_and_after_test" + "should_prepare_before_and_after_test" ); } @@ -58,7 +58,7 @@ void should_have_a_filesystem_friendly_name_container_has_started() { void static_container_should_have_a_filesystem_friendly_name_after_container_has_started() { assertThat(SHARED_CONTAINER.getLifecycleFilesystemFriendlyNames()) .containsExactly( - "org.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest-static" + "TestLifecycleAwareMethodTest" ); } From 261e4708d60d681b6660697affb55908c80e3f6a Mon Sep 17 00:00:00 2001 From: Alexander Sparkowsky Date: Sat, 4 Apr 2020 16:16:26 +0200 Subject: [PATCH 22/22] Generate filesystem friendly name from URLEncoded unique id (#1326) --- .../FilesystemFriendlyNameGenerator.java | 19 ++++++++++--------- .../FilesystemFriendlyNameGeneratorTest.java | 17 +++++++++++------ .../jupiter/TestLifecycleAwareMethodTest.java | 4 ++-- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java index 074be22c912..ef8560b3f19 100644 --- a/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java +++ b/modules/junit-jupiter/src/main/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGenerator.java @@ -2,22 +2,23 @@ import org.junit.jupiter.api.extension.ExtensionContext; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; + +import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.platform.commons.util.StringUtils.isBlank; class FilesystemFriendlyNameGenerator { private static final String UNKNOWN_NAME = "unknown"; - private static final String ALLOWED_CHARACTERS_REGEX = "[^\\w -]"; static String filesystemFriendlyNameOf(ExtensionContext context) { - String displayName = context.getDisplayName(); - if (isBlank(displayName)) { + String contextId = context.getUniqueId(); + try { + return (isBlank(contextId)) + ? UNKNOWN_NAME + : URLEncoder.encode(contextId, UTF_8.toString()); + } catch (UnsupportedEncodingException e) { return UNKNOWN_NAME; } - - return replaceIllegalCharacters(displayName.trim()); - } - - private static String replaceIllegalCharacters(String source) { - return source.replaceAll(ALLOWED_CHARACTERS_REGEX, ""); } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java index a1e6326a966..96617b0e063 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/FilesystemFriendlyNameGeneratorTest.java @@ -19,7 +19,7 @@ class FilesystemFriendlyNameGeneratorTest { void should_generate_filesystem_friendly_name(String displayName, String expectedName) { ExtensionContext context = mock(ExtensionContext.class); doReturn(displayName) - .when(context).getDisplayName(); + .when(context).getUniqueId(); String filesystemFriendlyName = filesystemFriendlyNameOf(context); @@ -30,11 +30,16 @@ private static Stream provideDisplayNamesAndFilesystemFriendlyNames() return Stream.of( Arguments.of("", "unknown"), Arguments.of(" ", "unknown"), - Arguments.of("not blank", "not blank"), - Arguments.of("abc ABC 1234567890", "abc ABC 1234567890"), - Arguments.of("no_umlauts_äöüÄÖÜéáíó", "no_umlauts_"), - Arguments.of("no\ttabs", "notabs"), - Arguments.of("no_special_[]{}/?<>!@#$%^&*()+=\\|'\";:`~", "no_special_") + Arguments.of("not blank", "not+blank"), + Arguments.of("abc ABC 1234567890", "abc+ABC+1234567890"), + Arguments.of( + "no_umlauts_äöüÄÖÜéáíó", + "no_umlauts_%C3%A4%C3%B6%C3%BC%C3%84%C3%96%C3%9C%C3%A9%C3%A1%C3%AD%C3%B3" + ), + Arguments.of( + "[engine:junit-jupiter]/[class:com.example.MyTest]/[test-factory:parameterizedTest()]/[dynamic-test:#3]", + "%5Bengine%3Ajunit-jupiter%5D%2F%5Bclass%3Acom.example.MyTest%5D%2F%5Btest-factory%3AparameterizedTest%28%29%5D%2F%5Bdynamic-test%3A%233%5D" + ) ); } } diff --git a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java index f3b4928a96e..cecdff490ef 100644 --- a/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java +++ b/modules/junit-jupiter/src/test/java/org/testcontainers/junit/jupiter/TestLifecycleAwareMethodTest.java @@ -50,7 +50,7 @@ void should_call_beforeTest_first_afterTest_later_with_filesystem_friendly_name( void should_have_a_filesystem_friendly_name_container_has_started() { assertThat(startedTestContainer.getLifecycleFilesystemFriendlyNames()) .containsExactly( - "should_prepare_before_and_after_test" + "%5Bengine%3Ajunit-jupiter%5D%2F%5Bclass%3Aorg.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest%5D%2F%5Bmethod%3Ashould_prepare_before_and_after_test%28%29%5D" ); } @@ -58,7 +58,7 @@ void should_have_a_filesystem_friendly_name_container_has_started() { void static_container_should_have_a_filesystem_friendly_name_after_container_has_started() { assertThat(SHARED_CONTAINER.getLifecycleFilesystemFriendlyNames()) .containsExactly( - "TestLifecycleAwareMethodTest" + "%5Bengine%3Ajunit-jupiter%5D%2F%5Bclass%3Aorg.testcontainers.junit.jupiter.TestLifecycleAwareMethodTest%5D" ); }