如何在 Spock 中为测试套件设置和清理资源
How to set up & clean up a resource in Spock for a test suite
我的 JUnit 套件中有这段代码:
@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();
}
}
我想将它重写为 Spock,但我找不到任何方法来进行全局设置,只有 setupSpec()
和 setup()
这还不够,因为我有多个规范和我只想启动一个容器。
我试着让套件保持原样并将 Spock 规范传递给它,但是 spock 测试被完全跳过(当我添加 extends Specifications
时)。可能是因为 Specification
有 @RunWith(Sputnik)
而它不能与 @RunWith(Suite)
一起玩,但我不确定如何解决这个问题。
是否有任何方法可以使用 Spock 进行全局设置或从 JUnit 套件执行 Spock 规范?
我的 pom.xml
(存根在那里,因为我混合了 java 和 groovy 代码):
<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>
总的来说,Spock 毕竟是一个 JUnit(这就是为什么 surefire 插件可以 运行 它而没有任何额外的麻烦),所以所有的套件方法都应该有效,虽然我没有使用这个功能。
此外,如果您有 "heavy" 共享资源,那么您可以尝试以下方法:
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
}
}
注意,共享资源是用"static"定义的,因此它只会在第一次测试访问它时创建一次。
作为处理此问题的不同策略:如果您的测试已经从另一个 class 继承,您可能需要考虑特征,或者将共享资源公开为单例,以便它保证只存在一个实例.
尝试 运行 这两个测试,您会发现 "Created shared source..." 行只被调用一次
答对的表扬属于Mark Bramnik当他写道:
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
虽然这是正确答案,但他答案中的示例代码指的是另一个场景。所以我打算在这里提供它以供参考。我不希望这个答案被接受,但如果其他人阅读它,他们也应该找到解释如何使用 Spock 中的功能的示例代码:
Spock 测试样本:
package de.scrum_master.Whosebug
class FooTest extends Specification {
def test() {
expect:
println "FooTest"
}
}
package de.scrum_master.Whosebug
class BarTest extends Specification {
def test() {
expect:
println "BarTest"
}
}
测试套件:
package de.scrum_master.Whosebug
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"
}
}
控制台日志:
Before suite
FooTest
BarTest
After suite
实际上,假设您的代码是静态的,在 spock 中是可能的。
要执行一次性初始化(启动容器、启动测试 zookeeper 集群或其他),只需创建一个静态单例容器,如下所示:
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
}
}
然后你需要一个抽象 class 用于你的集成测试,比如:
class IntegrationTest extends Specification {
def setup() {
init()
}
static void init () {
Postgres.init()
}
def cleanup() {
// do whatever you need
}
static void destroy() {
Postgres.destroy()
}
}
现在,清理有点棘手 - 特别是如果您有一些非守护线程阻止 jvm 关闭。这可能会导致您的测试套件挂起。
您可以使用 shutdownHoook
s 或使用 spock AbstractGlobalExtension
机制。
您实际上可以在 spock 执行所有规范后立即执行一些代码。
对于我们的 postgres 场景,我们有类似的东西:
class IntegrationTestCleanup extends AbstractGlobalExtension {
@Override
void stop() {
IntegrationTest.destroy()
}
}
缺少一个拼图才能使其正常工作 - 您需要在 src/test/resources/META-INF.services/org.spockframework.runtime.extension.IGlobalExtension
下提供一个引用您的扩展程序的特殊文件。该文件应包含指向您的扩展名的单行,例如
com.example.IntegrationTestCleanup
这将使 spock 识别它。请记住,它将在专用的 spock 线程中执行。
我确实意识到它有点重复已接受的答案,但我最近正在努力在 spock 中执行全局清理,所以我认为它可能有用。
我的 JUnit 套件中有这段代码:
@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();
}
}
我想将它重写为 Spock,但我找不到任何方法来进行全局设置,只有 setupSpec()
和 setup()
这还不够,因为我有多个规范和我只想启动一个容器。
我试着让套件保持原样并将 Spock 规范传递给它,但是 spock 测试被完全跳过(当我添加 extends Specifications
时)。可能是因为 Specification
有 @RunWith(Sputnik)
而它不能与 @RunWith(Suite)
一起玩,但我不确定如何解决这个问题。
是否有任何方法可以使用 Spock 进行全局设置或从 JUnit 套件执行 Spock 规范?
我的 pom.xml
(存根在那里,因为我混合了 java 和 groovy 代码):
<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>
总的来说,Spock 毕竟是一个 JUnit(这就是为什么 surefire 插件可以 运行 它而没有任何额外的麻烦),所以所有的套件方法都应该有效,虽然我没有使用这个功能。
此外,如果您有 "heavy" 共享资源,那么您可以尝试以下方法:
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
}
}
注意,共享资源是用"static"定义的,因此它只会在第一次测试访问它时创建一次。
作为处理此问题的不同策略:如果您的测试已经从另一个 class 继承,您可能需要考虑特征,或者将共享资源公开为单例,以便它保证只存在一个实例.
尝试 运行 这两个测试,您会发现 "Created shared source..." 行只被调用一次
答对的表扬属于Mark Bramnik当他写道:
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
虽然这是正确答案,但他答案中的示例代码指的是另一个场景。所以我打算在这里提供它以供参考。我不希望这个答案被接受,但如果其他人阅读它,他们也应该找到解释如何使用 Spock 中的功能的示例代码:
Spock 测试样本:
package de.scrum_master.Whosebug
class FooTest extends Specification {
def test() {
expect:
println "FooTest"
}
}
package de.scrum_master.Whosebug
class BarTest extends Specification {
def test() {
expect:
println "BarTest"
}
}
测试套件:
package de.scrum_master.Whosebug
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"
}
}
控制台日志:
Before suite
FooTest
BarTest
After suite
实际上,假设您的代码是静态的,在 spock 中是可能的。
要执行一次性初始化(启动容器、启动测试 zookeeper 集群或其他),只需创建一个静态单例容器,如下所示:
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
}
}
然后你需要一个抽象 class 用于你的集成测试,比如:
class IntegrationTest extends Specification {
def setup() {
init()
}
static void init () {
Postgres.init()
}
def cleanup() {
// do whatever you need
}
static void destroy() {
Postgres.destroy()
}
}
现在,清理有点棘手 - 特别是如果您有一些非守护线程阻止 jvm 关闭。这可能会导致您的测试套件挂起。
您可以使用 shutdownHoook
s 或使用 spock AbstractGlobalExtension
机制。
您实际上可以在 spock 执行所有规范后立即执行一些代码。
对于我们的 postgres 场景,我们有类似的东西:
class IntegrationTestCleanup extends AbstractGlobalExtension {
@Override
void stop() {
IntegrationTest.destroy()
}
}
缺少一个拼图才能使其正常工作 - 您需要在 src/test/resources/META-INF.services/org.spockframework.runtime.extension.IGlobalExtension
下提供一个引用您的扩展程序的特殊文件。该文件应包含指向您的扩展名的单行,例如
com.example.IntegrationTestCleanup
这将使 spock 识别它。请记住,它将在专用的 spock 线程中执行。
我确实意识到它有点重复已接受的答案,但我最近正在努力在 spock 中执行全局清理,所以我认为它可能有用。