From 92603f29d6a9bcdfe73ae0be64d2c2bed1eeda2b Mon Sep 17 00:00:00 2001 From: Sergei Egorov Date: Mon, 13 Apr 2020 09:59:52 +0200 Subject: [PATCH 1/2] Fix Spock's `TestLifecycleAware` integration --- .../spock/TestcontainersExtension.groovy | 2 +- .../TestcontainersMethodInterceptor.groovy | 30 +++---- .../spock/BrowserVncRecordingIT.groovy | 88 ------------------- .../TestLifecycleAwareContainerMock.java | 42 +++++++++ .../spock/TestLifecycleAwareIT.groovy | 56 ++++++++++++ 5 files changed, 113 insertions(+), 105 deletions(-) delete mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java create mode 100644 modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy index 1f66fd8f596..ca314354644 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy @@ -20,7 +20,7 @@ class TestcontainersExtension extends AbstractAnnotationDrivenExtension errors = [] @Override diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy index a70ec0d6e53..ef053a973dd 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy @@ -54,13 +54,6 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { @Override void interceptCleanupMethod(IMethodInvocation invocation) throws Throwable { - findAllTestLifecycleAwareContainers(invocation).each { - // we assume first error is the one we want - def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) - def testDescription = SpockTestDescription.fromTestDescription(invocation) - it.afterTest(testDescription, maybeException) - } - def containers = findAllContainers(false) stopContainers(containers, invocation) @@ -76,14 +69,6 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { } } - private List findAllTestLifecycleAwareContainers(IMethodInvocation invocation) { - spec.allFields.findAll { FieldInfo f -> - TestLifecycleAware.isAssignableFrom(f.type) - }.collect { - it.readValue(invocation.instance) as TestLifecycleAware - } - } - private List findAllComposeContainers(boolean shared) { spec.allFields.findAll { FieldInfo f -> DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared @@ -96,12 +81,25 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { if(!container.isRunning()){ container.start() } + + if (container instanceof TestLifecycleAware) { + def testDescription = SpockTestDescription.fromTestDescription(invocation) + (container as TestLifecycleAware).beforeTest(testDescription) + } } } - private static void stopContainers(List containers, IMethodInvocation invocation) { + private void stopContainers(List containers, IMethodInvocation invocation) { containers.each { FieldInfo f -> GenericContainer container = readContainerFromField(f, invocation) + + if (container instanceof TestLifecycleAware) { + // we assume first error is the one we want + def maybeException = Optional.ofNullable(errorListener.errors[0]?.exception) + def testDescription = SpockTestDescription.fromTestDescription(invocation) + (container as TestLifecycleAware).afterTest(testDescription, maybeException) + } + container.stop() } } diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy deleted file mode 100644 index 794032a58c4..00000000000 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/BrowserVncRecordingIT.groovy +++ /dev/null @@ -1,88 +0,0 @@ -package org.testcontainers.spock - -import org.intellij.lang.annotations.Language -import org.junit.Rule -import org.junit.rules.TemporaryFolder -import spock.lang.Specification -import spock.lang.Unroll -import spock.util.EmbeddedSpecRunner - -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_ALL -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode.RECORD_FAILING - -class BrowserVncRecordingIT extends Specification { - - @Rule - TemporaryFolder temp - - String recordingDir - - def setup() { - recordingDir = temp.getRoot().getAbsolutePath() - } - - @Unroll("For recording mode #recordingMode and failing test is #fails records video file named '#videoFileName'") - def "retains all recordings for RECORD_ALL if successful"() { - given: - - //noinspection GrPackage - @Language("groovy") - String myTest = """ -package org.testcontainers.spock - -import org.openqa.selenium.chrome.ChromeOptions -import org.openqa.selenium.remote.RemoteWebDriver -import org.testcontainers.containers.BrowserWebDriverContainer -import spock.lang.Specification - -import java.util.concurrent.TimeUnit - -import static org.testcontainers.containers.BrowserWebDriverContainer.VncRecordingMode - -@Testcontainers -class BrowserWebdriverContainerIT extends Specification { - - BrowserWebDriverContainer browserContainer = new BrowserWebDriverContainer() - .withCapabilities(new ChromeOptions()) - .withRecordingMode("$recordingMode" as VncRecordingMode, new File("$recordingDir")) - - RemoteWebDriver driver - - def setup() { - driver = browserContainer.getWebDriver() - driver.manage().timeouts().implicitlyWait(30, TimeUnit.SECONDS) - } - - def "should record"() { - when: - driver.get("http://en.wikipedia.org/wiki/Randomness") - - then: - driver.findElementByPartialLinkText("pattern").isDisplayed() == !$fails - } - -} - - -""" - - when: - EmbeddedSpecRunner runner = new EmbeddedSpecRunner(throwFailure: false) - runner.run(myTest) - - then: - def videoDir = temp.getRoot().list() - if (videoFileName.isEmpty()) { - videoDir.length == 0 - } else { - videoDir.find { it.contains(videoFileName) } - } - - where: - recordingMode | fails | videoFileName - RECORD_ALL | false | 'BrowserWebdriverContainerIT-should+record' - RECORD_FAILING | false | '' - RECORD_FAILING | true | 'BrowserWebdriverContainerIT-should+record' - } - -} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java new file mode 100644 index 00000000000..43be6d32ca4 --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareContainerMock.java @@ -0,0 +1,42 @@ +package org.testcontainers.spock; + +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.lifecycle.TestDescription; +import org.testcontainers.lifecycle.TestLifecycleAware; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +public class TestLifecycleAwareContainerMock extends GenericContainer implements TestLifecycleAware { + + static final String BEFORE_TEST = "beforeTest"; + static final String AFTER_TEST = "afterTest"; + + final List lifecycleMethodCalls = new ArrayList<>(); + final List lifecycleFilesystemFriendlyNames = new ArrayList<>(); + + Throwable capturedThrowable; + + @Override + public void beforeTest(TestDescription description) { + lifecycleMethodCalls.add(BEFORE_TEST); + lifecycleFilesystemFriendlyNames.add(description.getFilesystemFriendlyName()); + } + + @Override + public void afterTest(TestDescription description, Optional throwable) { + lifecycleMethodCalls.add(AFTER_TEST); + throwable.ifPresent(capturedThrowable -> this.capturedThrowable = capturedThrowable); + } + + @Override + public void start() { + // Do nothing + } + + @Override + public void stop() { + // Do nothing + } +} diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy new file mode 100644 index 00000000000..774af8cd9cf --- /dev/null +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy @@ -0,0 +1,56 @@ +package org.testcontainers.spock + +import org.intellij.lang.annotations.Language +import spock.lang.Specification +import spock.lang.Unroll +import spock.util.EmbeddedSpecRunner + +class TestLifecycleAwareIT extends Specification { + + @Unroll("When failing test is #fails afterTest receives '#filesystemFriendlyNames'") + def "lifecycle awareness"() { + given: + + //noinspection GrPackage + @Language("groovy") + String myTest = """ +package org.testcontainers.spock + +import org.testcontainers.containers.GenericContainer +import spock.lang.Specification + +@Testcontainers +class TestLifecycleAwareIT extends Specification { + + GenericContainer container = System.properties["org.testcontainers.container"] + + def "perform test"() { + expect: + "false" == System.getProperty("org.testcontainers.shouldFail") + } +} +""" + + when: + def container = new TestLifecycleAwareContainerMock() + def runner = new EmbeddedSpecRunner(throwFailure: false) + System.properties["org.testcontainers.container"] = container + System.setProperty("org.testcontainers.shouldFail", fails.toString()) + runner.run(myTest) + + then: + container.lifecycleMethodCalls == ["beforeTest", "afterTest"] + container.lifecycleFilesystemFriendlyNames.join(",") == filesystemFriendlyNames + if (errorMessageStartsWith) { + assert container.capturedThrowable.message.startsWith(errorMessageStartsWith) + } else { + assert container.capturedThrowable == null + } + + where: + fails | filesystemFriendlyNames | errorMessageStartsWith + false | 'TestLifecycleAwareIT-perform+test' | null + true | 'TestLifecycleAwareIT-perform+test' | "Condition not satisfied:" + } + +} From 8ca0696ff8e5e96877ece64b48bfde1726314caa Mon Sep 17 00:00:00 2001 From: Kevin Wittek Date: Mon, 13 Apr 2020 10:25:00 +0200 Subject: [PATCH 2/2] Small cleanups --- .../spock/TestcontainersExtension.groovy | 2 +- .../TestcontainersMethodInterceptor.groovy | 1 + .../spock/TestLifecycleAwareIT.groovy | 21 +++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy index ca314354644..1f66fd8f596 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersExtension.groovy @@ -20,7 +20,7 @@ class TestcontainersExtension extends AbstractAnnotationDrivenExtension errors = [] @Override diff --git a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy index ef053a973dd..16be002aeb1 100644 --- a/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy +++ b/modules/spock/src/main/groovy/org/testcontainers/spock/TestcontainersMethodInterceptor.groovy @@ -30,6 +30,7 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor { invocation.proceed() } + @Override void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable { def containers = findAllContainers(true) stopContainers(containers, invocation) diff --git a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy index 774af8cd9cf..18f7517ea3f 100644 --- a/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy +++ b/modules/spock/src/test/groovy/org/testcontainers/spock/TestLifecycleAwareIT.groovy @@ -7,38 +7,37 @@ import spock.util.EmbeddedSpecRunner class TestLifecycleAwareIT extends Specification { - @Unroll("When failing test is #fails afterTest receives '#filesystemFriendlyNames'") + @Unroll("When failing test is #fails, afterTest receives '#filesystemFriendlyNames' and throwable starting with message '#errorMessageStartsWith'") def "lifecycle awareness"() { given: - //noinspection GrPackage @Language("groovy") String myTest = """ -package org.testcontainers.spock - +import org.testcontainers.spock.Testcontainers import org.testcontainers.containers.GenericContainer import spock.lang.Specification @Testcontainers class TestLifecycleAwareIT extends Specification { - GenericContainer container = System.properties["org.testcontainers.container"] + GenericContainer container = System.properties["org.testcontainers.container"] as GenericContainer def "perform test"() { expect: - "false" == System.getProperty("org.testcontainers.shouldFail") + !System.properties["org.testcontainers.shouldFail"] } } """ - - when: + and: def container = new TestLifecycleAwareContainerMock() - def runner = new EmbeddedSpecRunner(throwFailure: false) System.properties["org.testcontainers.container"] = container - System.setProperty("org.testcontainers.shouldFail", fails.toString()) + System.properties["org.testcontainers.shouldFail"] = fails + + when: "executing the test" + def runner = new EmbeddedSpecRunner(throwFailure: false) runner.run(myTest) - then: + then: "mock container received lifecycle calls as expected" container.lifecycleMethodCalls == ["beforeTest", "afterTest"] container.lifecycleFilesystemFriendlyNames.join(",") == filesystemFriendlyNames if (errorMessageStartsWith) {