什么时候应该优先使用 Spock 的 @Shared 注释而不是静态字段?

When Spock's @Shared annotation should be preferred over a static field?

没有太多要补充的,整个问题都在标题中。

考虑 Spock 规范中使用的 class Foo 的这两个实例。

@Shared Foo foo1 = new Foo()

static Foo foo2 = new Foo()

总的来说,我知道 @Shared 注释背后的想法,但我想最好使用语言功能,在本例中是 static 字段。

是否有任何特定情况下应该优先选择另一个,或者这更像是一个品味问题?

Spock 就是表现力清晰度

Static 是一个 Java 关键字,仅显示 class 的内部结构(该字段对于所有实例都是相同的)

@Shared 是一个 Spock 特性,它告诉 reader 这个变量对于所有特性方法都是相同的。是专门针对单元测试的说明,让reader.

的单元测试更加清晰

主要的 Spock 块也是如此。如果你仔细想想,他们并没有真正改变代码上的任何东西。

public void myScenario(){
  int a = 2 + 3;
  assertEquals(5,a);
}

public void "simple addition scenario"(){
  when: "I add two numbers"
    int a = 2 +3

  then: "I expect the correct result"
  a == 5
}

两个单元测试在技术上做完全相同的事情。然而,第二个更清楚地表明了意图。 when:then: 标签除了阐明其意图外,实际上对代码没有任何作用。

总而言之,@Shared 使测试更具可读性。 (另见@Issue@Title等,它们的存在目的相同)

与 JUnit 相反,您必须在其中声明字段变量 static 并在

中为其赋值
@BeforeClass
public static void setupClass()

所以每个测试套件(不是每个方法)只初始化一次,在 Spock 中你可以使用实例字段变量并用 @Shared.

注释它

考虑以下示例:

class SharedTestSpec extends spock.lang.Specification {

    @Shared
    def shared = shared()

    def shared() {
        "I came from ${this.class.simpleName}"
    }

    def 'Test one'() {
        given:
            println("test one, shared: $shared")
        expect: true
    }

    def 'Test two'() {
        given:
            println("test two, shared: $shared")
        expect: true

    }
}

class SubclassSpec extends SharedTestSpec {

    @Override
    def shared() {
        println("They've got me!")
        "I came from ${this.class.simpleName}"
    }
}

运行 SubclassSpec 为您提供以下输出:

test one, shared: I came from SubclassSpec
test two, shared: I came from SubclassSpec
They've got me!

尽管无法解释打印顺序,但这是因为 AST。

作为一种更详尽的方法,这是一个带有输出的示例测试:

@Unroll
class BasicSpec extends Specification {
    
    int initializedVariable
    
    int globalVariable = 200
    
    static int STATIC_VARIABLE = 300
    
    @Shared
    int sharedVariable = 400
    
    void setup() {
        initializedVariable = 100
    }
    
    void 'no changes'() {
        expect:
            printVariables()
            /*
            initializedVariable: 100
            globalVariable: 200
            STATIC_VARIABLE: 300
            sharedVariable: 400
             */
    }
    
    void 'change values'() {
        setup:
            initializedVariable = 1100
            globalVariable = 1200
            STATIC_VARIABLE = 1300
            sharedVariable = 1400
        
        expect:
            printVariables()
            /*
            initializedVariable: 1100
            globalVariable: 1200
            STATIC_VARIABLE: 1300
            sharedVariable: 1400
             */
    }
    
    void 'print values again'() {
        expect:
            printVariables()
            /*
            initializedVariable: 100
            globalVariable: 200
            STATIC_VARIABLE: 1300
            sharedVariable: 1400
             */
    }
    
    private void printVariables() {
        println "initializedVariable: $initializedVariable"
        println "globalVariable: $globalVariable"
        println "STATIC_VARIABLE: $STATIC_VARIABLE"
        println "sharedVariable: $sharedVariable\n"
    }
}

令我惊讶的是,class' setup() 方法中的变量以及全局实例变量在每次测试时都会重置(大概是因为 class 为每个测试用例重新实例化)。同时,static@Shared 变量按预期工作。因此,后两者也可以在 where 子句中访问,这些子句在每个测试用例中列出的其他一些子句之前 运行。

静态字段只能用于常量。否则共享字段更可取,因为它们关于共享的语义定义更明确。