Testing Groovy单例和测试问题(使用Spock)

Testing Groovy单例和测试问题(使用Spock),testing,groovy,singleton,spock,Testing,Groovy,Singleton,Spock,有一个关于测试和单例的讨论。。。但这是关于Java模式的 我的问题是关于Groovy@Singleton(注释)实现此模式的方法 这似乎是另一个非常棒的优点。但是,当使用具有此注释的类进行测试(使用Spock)时,我遇到了一点问题 如果此实例的任何状态在测试过程中发生变化(从原始的、刚构建的状态),就我的实验所示,这将继续进行到下一个测试。。。我用几个测试测试了MySingletonClass.instance的hashCode(),结果都是一样的。也许这并不奇怪 但是。。。如果Spock能够(

有一个关于测试和单例的讨论。。。但这是关于Java模式的

我的问题是关于Groovy
@Singleton
(注释)实现此模式的方法

这似乎是另一个非常棒的优点。但是,当使用具有此注释的类进行测试(使用Spock)时,我遇到了一点问题

如果此实例的任何状态在测试过程中发生变化(从原始的、刚构建的状态),就我的实验所示,这将继续进行到下一个测试。。。我用几个测试测试了MySingletonClass.instance的
hashCode()
,结果都是一样的。也许这并不奇怪

但是。。。如果Spock能够(使用我只能推测的那种uber Groovy魔术)在测试之间以某种方式重置类,不是更好吗?即通过创建新实例

有一个明显的解决方法:将
reset
方法合并到每个
@Singleton
类中,在测试过程中,该类的状态可能会发生变化。然后调用
setup()
中的
reset
方法。。。事实上,我使用了一个通用的
规范
子类,
CommonProjectSpec
,从中我所有真正的
规范
子类。。。因此,这将是足够简单的实现

但这似乎有点不雅。还有其他选择吗?我是否应该将此作为斯波克建议的增强功能提交

PS它也证明了你不能对这个类做
Spy
(或者
GroovySpy
)。但您可以对其进行
Mock

    ConsoleHandler mockCH = Mock( ConsoleHandler ){
        getDriver() >> ltfm
    }
    GroovyMock( ConsoleHandler, global: true )
    ConsoleHandler.instance = mockCH

。。。是的,这里的“全局”
GroovyMock
实际上有能力“驯服”静态的
实例
字段,以便它温顺地接受嵌套中的
模拟
杜鹃。

所以基本上你想测试一个单体不是单体。我觉得这很奇怪。但无论如何,我把这个问题看作是一个谜题,我要解决它,因为这是一个很好的挑战(孩子们,不要在家里这样做!)

Groovy单例:

package de.scrum\u master.stackoverflow
@独生子女
高地人{
def计数=0
def战斗(){
println“只能有一个!”
计数++
doSomething()
}
def doSomething(){
打印“做点什么”
}
}
单例助手类:

package de.scrum\u master.stackoverflow
导入java.lang.reflect.Field
导入java.lang.reflect.Modifier
类GroovySingletonTool{
私人课堂
GroovySingletonTool(clazz类){
this.clazz=clazz
}
void setSingleton(T实例){
//使“实例”字段为非最终字段
Field=clazz.getDeclaredField(“实例”)
field.modifiers&=~Modifier.FINAL
//只有在以前未设置singleton实例时才有效
field.set(clazz.instance,instance)
}
void singleton(){
setSingleton(空)
}
void重新初始化Singleton(){
//取消设置singleton实例,否则后续构造函数调用将失败
单子()
setSingleton(clazz.newInstance())
}
}
Spock测试:

这个测试展示了如何

  • 在执行feature方法之前重新实例化Groovy单例
  • 对Groovy单例使用
    Stub()
  • 对Groovy单例使用
    Mock()
  • 对Groovy单例使用
    Spy()
package de.scrum\u master.stackoverflow
导入org.junit.Rule
导入org.junit.rules.TestName
导入spock.lang.Specification
导入spock.lang.Unroll
类HighlanderTest扩展了规范{
def singletonTool=新的GroovySingletonTool(Highlander)
@统治
TestName gebReportingSpecTestName
def设置(){
println“\n--$gebReportingSpecTestName.methodName--”
}
@展开
def“高地人战斗编号#战斗编号”(){
鉴于:
singletonTool.reinitialiseSingleton()
def highlander=highlander.instance
什么时候:
高地人战斗
然后:
highlander.count==1
哪里:
fightNo>{println“我是存根”}
}
singletonTool.setSingleton(高地浴缸)
def highlander=highlander.instance
什么时候:
高地人战斗
然后:
highlander==highlanderStub
哪里:
fightNo>{println“我只是在嘲笑你”}
}
singletonTool.setSingleton(高地锁)
def highlander=highlander.instance
什么时候:
高地人战斗
然后:
highlander==highlanderMock
0*highlander.doSomething()
哪里:

fightNo不幸的是,我在使用Kriegax的其他有用的解决方案时遇到了大问题

我做了很多实验,无法解释问题的来源。虽然有可能的线索。(顺便提一下,我尝试过在将新实例设置为singleton实例后立即应用修饰符更改的想法……这并没有解决问题)

在典型情况下,我发现我可能有一个
规范
,其中可能有15个特性(测试)。单独运行这些函数可以很好地工作:
MySingleton.instance
字段首先设置为
null
,然后设置为
MySingleton
的新实例

但是,当我尝试使用另一个xxx.groovy文件和另一个
规范运行它时,它可以运行大约8个功能。。。但后来我添加了一个新特性(即,我基本上是在对现有特性进行注释时),突然出现了问题:
MySingleton.instance
可以设置为
null
。。。但直接拒绝设置为新实例。我甚至尝试了一个
for
循环和
Thread.sleep()
,看看多次尝试是否可以解决这个问题

自然地,我接着看了一下刚才添加的令人不快的特性:但我没有看到任何特性