Unit testing 在gpars activeobject方法中调用Spock mock时,似乎从未调用过
在我的代码中,我有两个类,一个叫做DynamicRule,另一个叫做AORule,它为规则实例提供了一个GPARS-activeObject包装器 DynamicRule看起来有点像这样,它有一个名为dynamicExecute的方法Unit testing 在gpars activeobject方法中调用Spock mock时,似乎从未调用过,unit-testing,groovy,spock,gpars,active-objects,Unit Testing,Groovy,Spock,Gpars,Active Objects,在我的代码中,我有两个类,一个叫做DynamicRule,另一个叫做AORule,它为规则实例提供了一个GPARS-activeObject包装器 DynamicRule看起来有点像这样,它有一个名为dynamicExecute的方法 @InheritConstructors @Component @Slf4j //use Groovy AST to get logger @EqualsAndHashCode @Scope ("prototype") //IOC to delive
@InheritConstructors
@Component
@Slf4j //use Groovy AST to get logger
@EqualsAndHashCode
@Scope ("prototype") //IOC to deliver new instance on each injection
@Qualifier ("dynamicRule")
class DynamicRule extends BasicRule implements org.easyrules.api.Rule {
Closure dynamicEvaluation = null
Closure dynamicExecution = null
String sync = "sync"
... with method
void dynamicExecute () {
log.debug "dynamic execute: " + this.dump() + " : dynamic execute called : a rule.dynamicExecution was defined as > " + this.dynamicExecution
//if dynamic execution closure defined then call that
log.debug "dynamic execute: getting DynamicExecution reference "
def clos = this.getDynamicExecution()
//assert clos == this.dynamicExecution
log.debug "dynamic execute: rule.dynamicExecution using get() returns " + clos.dump()
if (clos) {
clos.call() //call execute action
log.debug "dynamic execute: dynamic rule closure was executed "
}
}....
我有一个用于此AORule的包装器类,它使用GPARS activeObject来管理DynamicRule
引用的状态,就像这样,execute()
方法调用异步activeObject方法来触发内部规则
引用上的操作
@Component
@Scope("prototype")
@ActiveObject
@Slf4j
@EqualsAndHashCode
class AORule implements org.easyrules.api.Rule {
//setup delegate dynamicRule and make it a part of this ActiveObject
//prototyped setter injection
//@Autowired
//@Scope ("prototype")
//@Qualifier ("dynamicRule")
@Delegate DynamicRule rule
AORule (name, description=null) {
log.debug "Create aorule.rule name/desc constructor called "
rule = new DynamicRule (name, description ?: "Basic Active Object Rule")
}
AORule () {
log.debug "Create aorule.rule default constructor called "
rule = new DynamicRule ()
}
@Autowired (/*required=true*/)
AORule (@Qualifier ("dynamicRule") DynamicRule dynrule) {
log.debug "Create aorule.rule injected rule constructor called "
rule = dynrule
}
....
void execute () {
active_execute()
}
/**
* Active method manages async action through object inside through hidden actor.
* variable 'rule' is the variable we are protecting. Runs either any dynmicExecution closure
* where defined or just runs the standard class execute method
*
* @return void
*/
@ActiveMethod
void active_execute () {
//rule.execute()
log.debug "activeExec : rule dump looks like " + rule.dump()
def val = rule.dynamicExecution
assert val == true
log.debug "activeExec : ret value of rule.getDynamicExecution() was > " + val
if (val != null) {
log.debug "activeExec : running dynamicExecution closure, where rule closure was " + rule?.dynamicExecution.toString()
rule.dynamicExecute() //should call method that executes closure, where this is defined
}
else {
log.debug "activeExec : running std Execution action "
rule.execute()
}
}
我一直在尝试写一些测试来证明这对我来说是可行的,但是失败了。我试着做了两个简单的测试,一个是dynamicRule本身,它可以工作-我设置了期望值,让Mock调用一个闭包,并测试该闭包对mockres
变量的副作用-一切正常,如预期:
def "set execution closure using spock mock but call stub directly when doing an execute action " () {
given:"setup of stub for testing "
def mockres = ""
def clos = {
mockres = "did nothing";
println "script: in stub closure, setting mockres to " + mockres + " mockres dump > " + mockres.dump();
}
//spock mock
DynamicRule stub = Mock ()
1*stub.dynamicExecute() >> {clos()} // pretend this has been set
when : "execution on stub directly "
stub.dynamicExecute()
then : "test execute closure ran as expected "
mockres == "did nothing"
}
在我的第二个测试中,我设置了Mock并在aorule.rule实例上设置它,并为Mock设置期望值,调用调用调用我的Mock的activeExecute()的aorule.execute()
方法
def "set execution closure using spock mock but call aorule.execute() when doing an execute action " () {
given:"setup of stub for testing "
def mockres = ""
def clos = {
mockres = "did nothing";
println "script: in stub closure, setting mockres to " + mockres + " mockres dump > " + mockres.dump();
}
//spock mock
DynamicRule stub = Mock ()
1*stub.getDynamicExecution() >> { true }
1*stub.dynamicExecute() >> {clos()} // pretend this has been set
println "test: save stub on aorule"
aorule.rule = stub
when : "call execute() on aorule.rule for stub "
//stub.dynamicExecute()
//aorule.rule.dynamicExecute() //works
println "test: call aorule.execute"
aorule.execute() //think this must be getting a new prototype object which is why not getting called
then : "test execute closure ran as expected "
mockres == "did nothing"
}
这失败了-为什么不在mock上运行closure,而then:assertion测试失败了
我在这里包含了部分输出跟踪-您可以看到aorule.execute()
触发了activeObject方法,我可以看到activeExec:rule dump的日志跟踪看起来像
-但是在这之后,它似乎停止了,没有额外的跟踪输出(例如,我希望看到val的值),并且存根闭包不会被调用,mockres也不会得到set-test失败
...
test: save stub on aorule
13:07:45.361 [main] DEBUG org.easyrules.spring.AORule - setting aorule.rule with > Mock for type 'DynamicRule' named 'stub'
test: call aorule.execute
13:07:45.367 [Actor Thread 1] DEBUG org.easyrules.spring.AORule - activeExec : rule dump looks like <org.easyrules.spring.DynamicRule$$EnhancerByCGLIB$$9b33215e@a4770a4 CGLIB$BOUND=false CGLIB$CALLBACK_0=org.spockframework.mock.runtime.CglibMockInterceptorAdapter@4809f9c CGLIB$CALLBACK_1=net.sf.cglib.proxy.NoOp$1@bf1bd4 dynamicEvaluation=null dynamicExecution=null sync=null name=null description=null priority=0>
13:07:45.441 [main] DEBUG o.s.t.c.s.AbstractDirtiesContextTestExecutionListener - After test method: context [DefaultTestContext@5c6648b0 testClass = AORuleSpecTest2, testInstance = org.easyrules.spring.AORuleSpecTest2@d41f816, testMethod = $spock_feature_0_1@AORuleSpecTest2, testException = Condition not satisfied:
mockres == "did nothing"
| |
"" false
11 differences (0% similarity)
(-----------)
(did nothing)
, mergedContextConfiguration = [MergedContextConfiguration@6f1de4c7 testClass = AORuleSpecTest2, locations = '{}', classes = '{class org.easyrules.spring.AORule, class org.easyrules.spring.DynamicRule}', contextInitializerClasses = '[]', activeProfiles = '{}', propertySourceLocations = '{}', propertySourceProperties = '{}', contextLoader = 'org.springframework.test.context.support.DelegatingSmartContextLoader', parent = [null]]], class annotated with @DirtiesContext [false] with mode [null], method annotated with @DirtiesContext [false] with mode [null].
Condition not satisfied:
调试器跳转到GPAR中某个位置的InternalActor.handleCurrentMessage()
错误处理程序中。如果我将此更改为
def val = rule.getDynamicExecution() //try to get value via groovy auo method
我不明白。也许我在做一些愚蠢的事情,但是我不能为我的@ActiveMethod-activeExecute()
编写一个测试来使用mock测试代码(我试图通过手工制作的类等来完成这项工作,但无法让模拟测试正常工作)
我怎样才能正确地更正和运行测试?据我所知,当我在脚本中运行代码(“实时”测试)时,代码似乎做了我希望它做的事情,等等,但我无法进行单元测试来显示这一点
Postscript:与ActiveMethod包装有关-不确定是什么
我在测试脚本中创建了两个虚拟包装器类,如下所示-一个不带ActiveObject,一个带ActiveObject支持-都调用嵌入规则上的dynamicExecute()
方法
class WrapperRule {
@Autowired //property injection
@Delegate DynamicRule rule
WrapperRule () {
}
WrapperRule (DynamicRule irule) {
rule = irule
assert rule
}
void execute () {
rule.dynamicExecute()
}
}
@ActiveObject
@Slf4j
@EqualsAndHashCode
class AOWrapperRule {
@Autowired //property injection
@Delegate DynamicRule rule
AOWrapperRule () {
}
AOWrapperRule (DynamicRule irule) {
rule = irule
assert rule
}
@ActiveMethod
void execute () {
rule.dynamicExecute()
}
}
同一脚本文件中的两个测试如下所示
def "set execution closure using spock mock on simple Wrapper rule when doing an execute action " () {
given:"setup of stub for testing "
def mockres = ""
def clos = {
mockres = "did nothing";
println "script: in stub closure, setting mockres to " + mockres + " mockres dump > " + mockres.dump();
}
WrapperRule wrule = new WrapperRule ()
//spock mock
DynamicRule stub = Mock ()
1*stub.dynamicExecute() >> {clos()} // pretend this has been set
println "test: save stub on wrule"
wrule.rule = stub
when : "call execute() on wrule.rule for stub "
println "test: call wrule.execute"
wrule.execute() //think this must be getting a new prototype object which is why not getting called
then : "test execute closure ran as expected "
mockres == "did nothing"
}
def "set execution closure using spock mock on AO Wrapper rule when doing an execute action " () {
given:"setup of stub for testing "
def mockres = ""
def clos = {
mockres = "did nothing";
println "script: in stub closure, setting mockres to " + mockres + " mockres dump > " + mockres.dump();
}
AOWrapperRule aowrule = new AOWrapperRule ()
//spock mock
DynamicRule stub = Mock ()
1*stub.dynamicExecute() >> {clos()} // pretend this has been set
println "test: save stub on aowrapper"
aowrule.rule = stub
when : "call execute() on aowrule.rule for stub "
println "test: call aowrule.execute"
aowrule.execute() //think this must be getting a new prototype object which is why not getting called
then : "test execute closure ran as expected "
mockres == "did nothing"
}
当我运行测试时-第一个测试通过得很好,第二个测试失败,并且没有调用我的clos
。接下来我应该尝试什么
对问题的澄清
我已经尝试将其简化为一个groovy测试文件——测试的类和测试都在同一个文件中,这样您就有希望看到发生了什么
package org.easyrules.spring
import groovy.mock.interceptor.StubFor
import groovy.transform.EqualsAndHashCode
import groovy.util.logging.Slf4j
import groovyx.gpars.activeobject.ActiveMethod
import groovyx.gpars.activeobject.ActiveObject
import org.junit.runner.RunWith
import org.softwood.RulesApplication
import org.springframework.beans.factory.annotation.Autowire
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.actuate.autoconfigure.ManagementContextConfiguration
import org.springframework.context.ApplicationContext
import org.springframework.test.context.ContextConfiguration
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner
import spock.lang.Specification
import spock.lang.Unroll
import spock.lang.*
/**
* Created by William on 10/03/2016.
*/
class TestRule {
Closure dynamicExecute = {println "default rule Execution "}
def exec () {
dynamicExecute()
}
}
class TestWrapperRule {
@Delegate TestRule rule
TestWrapperRule () {
rule = new TestRule()
}
void execute () {
rule.exec()
}
}
@ActiveObject
@Slf4j
@EqualsAndHashCode
class TestAOWrapperRule {
@Delegate TestRule rule
TestAOWrapperRule () {
rule = new TestRule()
}
@ActiveMethod
void execute () {
rule.exec()
}
}
class AORuleSpecAllInOneSpecTest extends Specification {
def "test simple closure setting mockres value - basics " () {
given:
def mockres = ""
def clos = {
mockres = "did nothing";
println "script: in stub closure, setting mockres to " + mockres + " mockres dump > " + mockres.dump();
}
when:
clos() //call closure
then: "test mockres was set when closure called "
mockres == "did nothing"
}
def "set execution closure using spock mock on simple Wrapper rule when doing an execute action " () {
given:"setup of stub for testing "
def mockres = ""
def clos = {
mockres = "did nothing";
println "script: in stub closure, setting mockres to " + mockres + " mockres dump > " + mockres.dump();
}
TestWrapperRule wrule = new TestWrapperRule ()
//spock mock
TestRule stub = Mock ()
//1*stub.exec() >> {clos()} // pretend this has been set
1*stub.getDynamicExecute() >> {clos()} // pretend this has been set
println "test: save stub on wrule"
wrule.rule = stub
when : "call execute() on wrule.rule for stub "
println "test: call wrule.execute"
//wrule.exec()
wrule.dynamicExecute
then : "test execute closure ran as expected "
mockres == "did nothing"
}
def "set execution closure using spock mock on AO Wrapper rule when doing an execute action " () {
given:"setup of stub for testing "
def mockres = ""
def clos = {
mockres = "did nothing";
println "script: in stub closure, setting mockres to " + mockres + " mockres dump > " + mockres.dump();
}
TestAOWrapperRule aowrule = new TestAOWrapperRule ()
//spock mock
TestRule stub = Mock ()
stub.exec() >> {clos()} // pretend this has been set
println "test: save stub on aowrapper"
aowrule.rule = stub
when : "call execute() on aowrule.rule for stub "
println "test: call aowrule.execute"
aowrule.execute()
//aowrule.exec() //if you then call the @delegate func without the activeMethod the test will work
then : "test execute closure ran as expected "
mockres == "did nothing"
}
}
需要注意的关键点是最后一个测试。我在Mock上设置了运行闭包输出的期望值,该闭包将更新mockres
变量,我可以断言该变量已在then:子句中设置
当您调用@ActiveMethod execute()
时,测试将像这样失败
...script: in stub closure, setting mockres to did nothing mockres dump > <java.lang.String@c141192c value=did nothing hash=-1052698324>
Condition not satisfied:
mockres == "did nothing"
| |
"" false
11 differences (0% similarity)
(-----------)
(did nothing)
…脚本:在存根闭包中,将mockres设置为nothing mockres dump>
不满足的条件:
mockres==“什么也没做”
| |
”“错
11个差异(0%相似性)
(-----------)
(什么也没做)
这表明mockres不是由闭包调用设置的
如果取消注释后面的aowrule.exec()行-然后直接调用委派的exec()
调用-不使用ActiveMethod使用的隐藏角色。当运行下一行时,将正确调用闭包并设置mockres
因此,问题在于,通过@ActiveMothod
后面隐藏的参与者来测试mock是否有效
我认为在现实中,如果只是作为脚本运行(代码中的实际使用),代码正在做我期望的事情。然而,我似乎无法编写测试来证明调用ActiveMethod时的预期行为
我是否需要做一些聪明的事情来为异步代码(如GPAR)编写一个带有模拟的spock测试?或者这是一个应该可以工作但不能工作的东西。我在spock测试脚本中添加了两个新测试。一个简单的dynamicRule包装器和一个带有ActiveObject(GPAR)的包装器支持。没有活动对象的测试可以正常工作。使用AOWrapper的测试无法正确执行-不知道现在该去哪里-这是GPAR的错误还是我不能使用spock测试异步代码?您应该尝试提取一个小一些的可复制示例。您的问题很难阅读和理解;-)好的-我已经欺骗并简化了它,所以所有的类和测试都在一个groovy文件中-这样你可以看到结构-然后是测试-请参阅上面问题的新附录来了解这一点
...script: in stub closure, setting mockres to did nothing mockres dump > <java.lang.String@c141192c value=did nothing hash=-1052698324>
Condition not satisfied:
mockres == "did nothing"
| |
"" false
11 differences (0% similarity)
(-----------)
(did nothing)