Spock:如何在规范的实现之间正确共享特性

Spock: How to properly share features between implementations of Specification

我遇到了 Spock Framework (1.3-groovy-2.5) 的一个不幸方面,我将其用于 Gradle 插件的集成测试。

代码示例

Parent class:

class ClassA extends Specification {

    def setupSpec() {
        System.out.println("In ClassA setupSpec()")
    }

    def "base feature"() {
        expect:
        true
    }
}

Child class:

class ClassB extends ClassA {

    @Override
    def setupSpec() {
        System.out.println("In ClassB setupSpec()")
    }

    def "extended feature"() {
        expect:
        true
    }
}

当我 运行 在 ClassB 中测试时,setupSpec() 的两个版本都被调用:

In ClassA setupSpec()
In ClassB setupSpec()

当然,如果我通过本机 Groovy 方式调用方法:

class Main {

    static void main(String[] args) {
        ClassB classB = new ClassB()
        classB.setupSpec()
    }
}

然后我只看到预期的内容:

In ClassB setupSpec()

所以,很明显,这是某种 Spock 功能。

问题

在实践中,在覆盖设置逻辑的同时从 Specification 的实现继承的建议方法是什么?

documented一样,fixture方法并不是要相互覆盖,而是要相互补充,即它们都是按照特定的逻辑顺序调用的。正如您所说,这是一项功能。

因此,我的回答是:没有建议的方法来覆盖设置逻辑。相反,建议的方法是以不需要覆盖的方式设计您的基础和派生规范。我这样做从来没有遇到任何问题,希望您也能轻松解决问题。

您的示例代码过于简略,无法再多说什么,但基本上考虑基础 类 中的夹具方法负责设置和清理其中的夹具字段,而派生规范的夹具方法将处理特定于那些 类 的额外固定装置。在您的示例中,基本规范中还有一个功能方法,我觉得这很奇怪,因为它会针对基本规范本身执行,并且每次执行派生规范时都会执行。我宁愿将基本规范视为实际上抽象的(避免向它们添加任何功能方法并避免它们被测试运行器拾取),但我相信在某些情况下,您上面勾勒的内容可能也会有所帮助。我只是想不出一个。

Fixture Method Invocation Order

中所述

If fixture methods are overridden in a specification subclass then setup() of the superclass will run before setup() of the subclass. cleanup() works in reverse order, that is cleanup() of the subclass will execute before cleanup() of the superclass. setupSpec() and cleanupSpec() behave in the same way. There is no need to explicitly call super.setup() or super.cleanup() as Spock will automatically find and execute fixture methods at all levels in an inheritance hierarchy.

最简单的方法是将逻辑移动到您可以覆盖的另一个方法。

class Base extends Specification {
  def setupSpec() {
    init()
  }
  
  def init() {
    println "foo"
  }

  def "let's try this!"() {
    expect:
    Math.max(1, 2) == 2
  }
}


class Sub extends Base {
  @Override
  def init() {
    println "bar"
  }
}