Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Spock's TestLifecycleAware integration #2563

Merged
merged 2 commits into from Apr 13, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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:"
}

}