Reputation: 5592
I have this code in my JUnit suite:
@RunWith(Suite.class)
@Suite.SuiteClasses({ MyJavaTest.class, MyAnotherJavaTest.class })
public class MyIntegrationSuite {
@BeforeClass
public static void beforeTests() throws Exception {
Container.start();
}
@AfterClass
public static void afterTests() throws Exception {
Container.stop();
}
}
I want to rewrite it to Spock, but I can't find any way to do a global setup, only setupSpec()
and setup()
which is not enough, since I have multiple specifications and I want to start a Container only once.
I tried leaving the suite as it is and passing Spock specifications to it, but the spock tests are skipped entirely (when I add extends Specifications
). It's probably, because Specification
has @RunWith(Sputnik)
and it doesn't play with @RunWith(Suite)
, but I'm not sure how to get around that.
Is there any way to do a global setup with Spock or execute Spock specifications from JUnit suite?
My pom.xml
(stubs are there, because I'm mixing java and groovy code):
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>${buildhelper.plugin.version}</version>
<executions>
<execution>
<id>add-groovy-test-source</id>
<phase>test</phase>
<goals>
<goal>add-test-source</goal>
</goals>
<configuration>
<sources>
<source>${basedir}/src/test/groovy</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.gmavenplus</groupId>
<artifactId>gmavenplus-plugin</artifactId>
<version>${gmavenplus.plugin.version}</version>
<executions>
<execution>
<goals>
<goal>generateTestStubs</goal>
<goal>compileTests</goal>
<goal>removeTestStubs</goal>
</goals>
</execution>
</executions>
</plugin>
Upvotes: 3
Views: 4511
Reputation: 1919
Actually, it is possible in spock, assuming your code could be static.
To perform one-time initialization (spinup a container, start a test zookeeper cluster or whatever), just create a static singleton holder like the following:
class Postgres {
private static PostgresContainer container
static void init() {
if (container != null)
return
container = new PostgresContainer()
container.start()
}
static void destroy() {
if (container == null)
return
container.stop()
container = null
}
}
Then you need an abstract class for your integration tests, something like:
class IntegrationTest extends Specification {
def setup() {
init()
}
static void init () {
Postgres.init()
}
def cleanup() {
// do whatever you need
}
static void destroy() {
Postgres.destroy()
}
}
Now, cleanup is a little bit tricky - specially if you have some non-daemon threads preventing jvm from shutting down. This might cause your test suite to hang.
You can either use shutdownHoook
s or use the spock AbstractGlobalExtension
mechanism.
You can actually execute some code right after spock executes all the specs.
For our postgres scenario, we'd have something like:
class IntegrationTestCleanup extends AbstractGlobalExtension {
@Override
void stop() {
IntegrationTest.destroy()
}
}
There's one missing puzzle to make it work - you need to provide a special file under src/test/resources/META-INF.services/org.spockframework.runtime.extension.IGlobalExtension
that references your extension. That file should contain a single line pointing to your extension, like
com.example.IntegrationTestCleanup
This will make spock recognize it. Keep in mind it will be executed in dedicated spock thread.
I do realize it kind of duplicates the accepted answer, but I was recently struggling with performing a global cleanup in spock so I thought it could be useful.
Upvotes: 1
Reputation: 67327
The praise for the right answer belongs to Mark Bramnik when he wrote:
In general, Spock is a JUnit After all (that's why surefire plugin can run it without any additional hassle), so all approaches to the Suites should work
While this is the correct answer, the sample code in his answer refers to another scenario. So I am going to provide it here for reference. I do not expect this answer to get accepted, but if others read it they also should find sample code explaining how to use the feature in Spock:
Sample Spock tests:
package de.scrum_master.stackoverflow
class FooTest extends Specification {
def test() {
expect:
println "FooTest"
}
}
package de.scrum_master.stackoverflow
class BarTest extends Specification {
def test() {
expect:
println "BarTest"
}
}
Test suite:
package de.scrum_master.stackoverflow
import org.junit.AfterClass
import org.junit.BeforeClass
import org.junit.runner.RunWith
import org.junit.runners.Suite
import spock.lang.Specification
@RunWith(Suite.class)
@Suite.SuiteClasses([FooTest, BarTest])
class SampleTestSuite {
@BeforeClass
static void beforeTests() throws Exception {
println "Before suite"
}
@AfterClass
static void afterTests() throws Exception {
println "After suite"
}
}
Console log:
Before suite
FooTest
BarTest
After suite
Upvotes: 2
Reputation: 42491
In general, Spock is a JUnit After all (that's why surefire plugin can run it without any additional hassle), so all approaches to the Suites should work, although I haven't used this feature.
In addition, If you have a "heavy" shared resource, then you might try the following approach:
abstract class SharedResourceSupport extends Specification {
def static sharedSource = new SharedSource()
}
class SharedSource {
public SharedSource() {
println "Created shared source. Its our heavy resource!"
}
}
class SpockTest1 extends SharedResourceSupport {
def "sample test" () {
expect:
sharedSource != null
}
}
class SpockTest2 extends SharedResourceSupport {
def "another test" () {
expect:
sharedSource != null
}
}
Note that the shared resource is defined with "static", so that it will be created only once when the first test will access it.
As different strategies to deal with this: You might want to consider traits, if your test already inherits from another class, or expose shared resource as a Singleton so that it will guarantee that only one instance exists.
Try to run both tests and you'll see that that the line "Created shared source..." is called only once
Upvotes: 2