Skip to content

Commit

Permalink
Fix Spock's TestLifecycleAware integration (#2563)
Browse files Browse the repository at this point in the history
* Fix Spock's `TestLifecycleAware` integration

* Small cleanups

Co-authored-by: Kevin Wittek <kevin@wittek.dev>
  • Loading branch information
bsideup and kiview committed Apr 13, 2020
1 parent 95c828d commit 6adb23b
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 104 deletions.
Expand Up @@ -30,6 +30,7 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor {
invocation.proceed()
}

@Override
void interceptCleanupSpecMethod(IMethodInvocation invocation) throws Throwable {
def containers = findAllContainers(true)
stopContainers(containers, invocation)
Expand All @@ -54,13 +55,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)

Expand All @@ -76,14 +70,6 @@ class TestcontainersMethodInterceptor extends AbstractMethodInterceptor {
}
}

private List<TestLifecycleAware> findAllTestLifecycleAwareContainers(IMethodInvocation invocation) {
spec.allFields.findAll { FieldInfo f ->
TestLifecycleAware.isAssignableFrom(f.type)
}.collect {
it.readValue(invocation.instance) as TestLifecycleAware
}
}

private List<FieldInfo> findAllComposeContainers(boolean shared) {
spec.allFields.findAll { FieldInfo f ->
DockerComposeContainer.isAssignableFrom(f.type) && f.shared == shared
Expand All @@ -96,12 +82,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<FieldInfo> containers, IMethodInvocation invocation) {
private void stopContainers(List<FieldInfo> 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()
}
}
Expand Down

This file was deleted.

@@ -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<TestLifecycleAwareContainerMock> implements TestLifecycleAware {

static final String BEFORE_TEST = "beforeTest";
static final String AFTER_TEST = "afterTest";

final List<String> lifecycleMethodCalls = new ArrayList<>();
final List<String> 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> throwable) {
lifecycleMethodCalls.add(AFTER_TEST);
throwable.ifPresent(capturedThrowable -> this.capturedThrowable = capturedThrowable);
}

@Override
public void start() {
// Do nothing
}

@Override
public void stop() {
// Do nothing
}
}
@@ -0,0 +1,55 @@
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' and throwable starting with message '#errorMessageStartsWith'")
def "lifecycle awareness"() {
given:

@Language("groovy")
String myTest = """
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"] as GenericContainer
def "perform test"() {
expect:
!System.properties["org.testcontainers.shouldFail"]
}
}
"""
and:
def container = new TestLifecycleAwareContainerMock()
System.properties["org.testcontainers.container"] = container
System.properties["org.testcontainers.shouldFail"] = fails

when: "executing the test"
def runner = new EmbeddedSpecRunner(throwFailure: false)
runner.run(myTest)

then: "mock container received lifecycle calls as expected"
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:"
}

}

0 comments on commit 6adb23b

Please sign in to comment.