如何测试某个字段是否设置为某个值?

时间:2018-05-07 18:24:52

标签: groovy field spock getter-setter

这似乎是基本的,所以我期待这是一个骗局...但我还没有找到任何回答这个问题的东西。

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

def something

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

something = null 

something = new Bubble()

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

then区块中,我尝试过:

1 * spyCUT.setSomething( null ) 

1 * spyCUT.setSomething(_)

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

顺便说一句,为了回答我可以在something块中测试then的值的反对意见,情况是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', _ )

1 个答案:

答案 0 :(得分:1)

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

package de.scrum_master.stackoverflow

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/stackoverflow/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:您想知道为什么会触发设置者调用:

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

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

实际上我认为即使没有查看字节码也不难理解,只需遵循通常的Groovy教程。我正在重复自己,但我确实认为你过度工程并且过于复杂化了一些事情,对事情进行了太深入的测试。我不会把这样的测试放到生产代码库中。尝试测试类的行为(考虑规范和功能!),而不是内部的复杂性。但如果它能帮助你理解Groovy的工作方式,那就继续玩吧。

截至目前,请不要进一步提出问题修改和后续问题。如果您遇到新问题,最好使用新的MCVE创建新问题。