如何测试字段是否设置为特定值?

How can I test that a field is set to a certain value?

这看起来很基本,所以我希望这是一个骗局...但我还没有找到任何可以回答这个问题的东西。

我的应用代码也是Groovy。假设我有一个字段

def something

并且在我的测试中(CUT 是一个 Spock Spy)我 运行 一个中间有一行的方法

something = null 

something = new Bubble()

...我只是想找到一种方法来测试确实已将某些内容设置为 null(或任何值...)

在我的 then 块中,我尝试过:

1 * spyCUT.setSomething( null ) 

1 * spyCUT.setSomething(_)

1 * spyCUT.set( 'something', _ )

顺便说一句,为了回答我可以在 then 块中测试 something 的值的反对意见,情况是 something 应该首先设置为一个值然后在这个方法的过程中到另一个...

读过 Groovy In Action 2nd Ed 我对 Groovy 如何处理获取和设置字段有最模糊的概念...显然不够。

MCVE(FWIW!)

class Spocko {
    def something

    def doStuff() {
        something = 'fruit'
    }
}

class SpockoTest extends Specification {
    def 'test it'(){
        given:
        Spocko spySpocko = Spy( Spocko )

        when:
        spySpocko.doStuff()

        then:
        1 * spySpocko.setSomething(_)
    }
}

稍后(在 kriegaex 非常有帮助的回复之后)

上面 SpockTest 其中 setSomething 调用:

class Spocko {
    def something

    def doStuff() {
        this.each{
            it.something = 'fruit' 
        }
    }
}

...通过!我现在正在努力理解为什么...

顺便说一句,我还发现以下通过(并且没有关闭):

1 * spySpocko.setProperty( 'something', _ )

看了你的MCVE之后,问题可以这样回答:你不能测试一个从未发生过的方法调用。 doStuff() 只是给字段赋值,它不会在内部调用 setter 方法。看看这个:

package de.scrum_master.Whosebug

import spock.lang.Specification

class SpockoTest extends Specification {
  static class Spocko {
    def something

    def doStuff() {
      something = 'fruit'
    }

    def doMoreStuff() {
      setSomething('vegetable')
    }
  }

  def 'test it'(){
    given: 'Spocko spy'
    Spocko spySpocko = Spy(Spocko)

    when: 'calling method assigning value to property'
    spySpocko.doStuff()

    then: 'no setter is called'
    0 * spySpocko.setSomething(_)
    spySpocko.something == 'fruit'

    when: 'calling method using setter'
    spySpocko.doMoreStuff()

    then: 'setter gets called'
    1 * spySpocko.setSomething('vegetable')

    when: 'using Groovy setter-like syntax from another class'
    spySpocko.something = 'fish'

    then: 'actually a setter gets called'
    1 * spySpocko.setSomething('fish')
  }
}

事情是这样的。调用时

javap -v target/test-classes/de/scrum_master/Whosebug/SpockoTest$Spocko.class

你看(输出缩短):

public java.lang.Object doStuff();
  descriptor: ()Ljava/lang/Object;
  flags: ACC_PUBLIC
  Code:
    stack=2, locals=3, args_size=1
       0: invokestatic  #24                 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
       3: astore_1
       4: ldc           #36                 // String fruit
       6: astore_2
       7: aload_2
       8: aload_0
       9: swap
      10: putfield      #38                 // Field something:Ljava/lang/Object;
      13: aload_2
      14: areturn
      15: aconst_null
      16: areturn

public java.lang.Object doMoreStuff();
  descriptor: ()Ljava/lang/Object;
  flags: ACC_PUBLIC
  Code:
    stack=3, locals=2, args_size=1
       0: invokestatic  #24                 // Method $getCallSiteArray:()[Lorg/codehaus/groovy/runtime/callsite/CallSite;
       3: astore_1
       4: aload_1
       5: ldc           #40                 // int 0
       7: aaload
       8: aload_0
       9: ldc           #42                 // String vegetable
      11: invokeinterface #48,  3           // InterfaceMethod org/codehaus/groovy/runtime/callsite/CallSite.callCurrent:(Lgroovy/lang/GroovyObject;Ljava/lang/Object;)Ljava/lang/Object;
      16: areturn
      17: aconst_null
      18: areturn

你能看出区别吗?


问题编辑 2 后更新:您想知道为什么这会触发 setter 调用:

def doStuff() {
  this.each {
    it.something = 'fruit' 
  }
}

这是因为 this 作为参数提供给闭包,因此 it.something = 'fruit' 像我的示例一样动态解析 spySpocko.something = 'fish' 因为它不是内部赋值something = 'fruit'(相当于this.something = 'fruit')了。

其实我觉得即使不看字节码也不难理解,只要按照通常的Groovy教程。我在重复我自己,但我确实认为你有点过度工程化和过度复杂化,测试太深了。我不会将这些测试放入生产代码库中。尝试测试您的 类 的行为(想想规格和功能!),而不是内部的复杂性。但如果它能帮助您了解 Groovy 的工作原理,请继续玩。

截至目前,请避免进一步的问题编辑和后续问题。如果你有新问题,最好用新的MCVE创建一个新问题。