如何设置&清理Spock中的资源以获得测试套件

时间:2018-05-14 16:22:20

标签: groovy junit 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()这还不够,因为我有多个规格和我想只启动一次Container。

我尝试将套件保留原样并将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>

3 个答案:

答案 0 :(得分:2)

正确答案的赞美属于Mark Bramnik,当时他写道:

  

一般来说,Spock毕竟是一个JUnit(这就是为什么surefire插件可以在没有任何额外麻烦的情况下运行它),所以套件的所有方法都应该工作

虽然这是正确的答案,但他的答案中的示例代码指的是另一种情况。所以我将在此提供它以供参考。我不希望这个答案被接受,但如果其他人阅读它们,他们也应该找到解释如何在Spock中使用该功能的示例代码:

示例Spock测试:

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"
  }
}

测试套件:

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"
  }
}

控制台日志:

Before suite
FooTest
BarTest
After suite

答案 1 :(得分:1)

一般来说,Spock毕竟是一个JUnit(这就是为什么surefire插件可以在没有任何额外麻烦的情况下运行它),所以套件的所有方法都应该有效,尽管我还没有使用过这个功能。

此外,如果你有一个&#34;重&#34;共享资源,然后您可以尝试以下方法:

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
    }
}

请注意,共享资源是使用&#34; static&#34;定义的,因此只有在第一个测试访问它时才会创建一次。

作为处理此问题的不同策略:如果您的测试已经从另一个类继承,或者将共享资源公开为Singleton,那么您可能需要考虑特征,以便它保证只存在一个实例。

尝试运行这两个测试,您将看到该行&#34;创建共享源...&#34;只被召唤一次

答案 2 :(得分:0)

实际上,假设您的代码是静态的,则有可能在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 IntegrationTest extends Specification {

    def setup() {
        init()
    }

    static void init () {
        Postgres.init()
    }

    def cleanup() {
        // do whatever you need
    }

    static void destroy() {
        Postgres.destroy()
    }
}

现在,清理有些棘手-特别是如果您有一些非守护程序线程阻止jvm关闭。这可能会导致您的测试套件挂起。 您可以使用shutdownHoook或使用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进行全局清理方面很费力,所以我认为它可能有用。