这似乎是基本的,所以我期待这是一个骗局...但我还没有找到任何回答这个问题的东西。
我的应用代码也是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', _ )
答案 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创建新问题。