Groovy Singleton和测试问题(与Spock一起使用) [英] Groovy Singleton and testing issue (with Spock)
问题描述
在此处上有关于测试和单例的讨论……但这是关于Java模式的讨论.
There's a discusson here about testing and singletons... but that is about Java patterns.
我的问题特别是关于Groovy @Singleton
(注释)实现此模式的方式.
My question is specifically about the Groovy @Singleton
(annotation) way of implementing this pattern.
这似乎又是Groovy Goodness的另一点.但是,在使用具有此批注的类进行测试(使用Spock)时,我有一个问题.
This seems like another bit of Groovy Goodness. But I have a bit of a problem when testing (with Spock) using a class which has this annotation.
如果该实例的任何状态在测试期间发生变化(从原始的刚刚构建的状态开始),只要我的实验表明这将继续进行下一个测试...我测试了MySingletonClass.instance
' s hashCode()
进行了几次测试,它们均返回相同的状态.也许这并不奇怪.
If any of the state of this instance changes during a test (from the pristine, just-constructed state), as far as my experiments indicate this will then carry through to the next test... I tested MySingletonClass.instance
's hashCode()
with several tests and they all came back the same. Perhaps this isn't surprising.
但是...如果Spock能够(使用我只能推测的那种超级格罗维魔术)以某种方式重置测试之间的类,那会不会更好呢? IE.通过创建一个新实例?
But ... wouldn't it be better if Spock could (using the kind of uber-Groovy magic I can only speculate on) somehow reset the class between tests? I.e. by creating a new instance?
有一个明显的解决方法:将reset
方法合并到每个@Singleton
类中,在测试过程中其状态可能会更改.然后在setup()
中调用该reset
方法...实际上,我使用了一个常见的Specification
子类CommonProjectSpec
,所有我的真正的Specification
子类都来自该子类...这样就足够简单了实施.
There is an obvious workaround: to incorporate a reset
method into each @Singleton
class where its state might change during a test. And then call that reset
method in setup()
... in fact I use a common Specification
subclass, CommonProjectSpec
, from which all my real Specification
s subclass... so that would be simple enough to implement.
但是似乎有点不雅致.还有其他选择吗?我应该将其作为Spock建议的增强功能提交吗?
But it seems a bit inelegant. Is there any other option? Should I maybe submit this as a Spock suggested enhancement?
PS事实证明,您不能再创建此类的Spy
(或GroovySpy
).但是您可以将其制成Mock
:
PS it also turns out you can't then make a Spy
of this class (or a GroovySpy
). But you can make a Mock
of it:
ConsoleHandler mockCH = Mock( ConsoleHandler ){
getDriver() >> ltfm
}
GroovyMock( ConsoleHandler, global: true )
ConsoleHandler.instance = mockCH
...是的,这里的全局" GroovyMock
实际上具有驯服"静态instance
字段的能力,这样它就可以轻松地在巢中接受Mock
杜鹃.
... yes, the "global" GroovyMock
here actually has the ability to "tame" the static instance
field so that it meekly accepts a Mock
cuckoo in the nest.
推荐答案
不幸的是,我遇到了Kriegax原本有用的解决方案的大问题.
Unfortunately I ran into big problems with Kriegax's otherwise helpful solution.
我做了很多实验,无法解释问题的根源.尽管有可能在此处提供了线索. (顺便说一句,在将新实例设置为单例实例之后,我确实尝试过立即应用修改器更改的想法,但这并不能解决问题.)
I've done quite a bit of experimentation and have been unable to explain where the problem comes from. Although there is a possible clue here. (Incidentally I did try this idea of applying the change of modifier immediately after setting the new instance to be the singleton instance... it did not solve the problem).
在典型情况下,我发现我可能有一个带有15个功能(测试)的Specification
.单独运行它们可以正常工作:MySingleton.instance
字段首先设置为null
,然后设置为MySingleton
的新实例.
In a typical situation I find I may have a Specification
with maybe 15 features (tests). Running these on their own works fine: the MySingleton.instance
field is set first to null
and then to a new instance of MySingleton
.
但是当我尝试使用另一个Specification
的另一个xxx.groovy文件运行该程序时,它将对大约8个功能正常运行...但是我添加了一个新功能(即,我基本上对现有功能不加注释)正如我所见)突然出现了问题:MySingleton.instance
可以设置为null
...,但是拒绝将点空白设置为新实例.我什至尝试了for
循环和Thread.sleep()
来查看多次尝试是否可以解决它.
But then when I try to run this with another xxx.groovy file with another Specification
, it will work OK for about 8 features ... but then I add a new feature (i.e. I'm basically uncommenting existing features as I go) suddenly the problem crops up: MySingleton.instance
can be set to null
... but refuses point blank to be set to a new instance. I even tried a for
loop with a Thread.sleep()
to see if trying multiple times might solve it.
自然地,我然后看看刚刚添加的令人反感的功能:但是,没有什么是我在其他功能中没有做过的.更糟糕的是,接下来的事情是:我然后发现这些结果不一致:有时,有问题的"新功能(一旦取消注释)就不会在另一个.groovy文件中触发Field.set( ... )
的失败.顺便说一下,在任何情况下Field.set( ... )
都不会抛出Exception
.
Naturally I then had a look at the offending feature which had just been added: but there was nothing there that I hadn't done in other features. Worse, far worse, follows: I then find that these results are not consistent: sometimes the "offending" new feature, once uncommented, does NOT then trigger the failure of Field.set( ... )
in the other .groovy file. By the way, no Exception
is thrown by Field.set( ... )
during any of this.
应该指出的是,field.modifiers &= ~Modifier.FINAL
据说是黑客",如此处,例如,有关其用法的许多警告.
It should be noted, en passant, that field.modifiers &= ~Modifier.FINAL
is said to be "a hack", as described here, for example, with many caveats about its use.
因此,我很不情愿地得出以下结论:如果要使用Groovy拥有一个或多个单例类,则必须拥有一个reset
方法,该方法可以保证将实例返回到原始状态(新构造) )状态,否则您必须放弃使用@Singleton
注释(即,如果您热衷于为每个功能构建一个新实例).
I've therefore reluctantly come to the conclusion that if you want to have one or more singleton classes with Groovy you either have to have a reset
method, which can be guaranteed to return the instance to a pristine (newly constructed) state, or you have to abandon use of the @Singleton
annotation (i.e. if you are keen to construct a new instance with each feature).
这篇关于Groovy Singleton和测试问题(与Spock一起使用)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!