In my code I have two classes, one a thing called DynamicRule, and the other a wrapper for this called AORule which provides a GPARS activeObject wrapper for a rule instance.
The DynamicRule looks a bit like this - with a method called dynamicExecute
@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 "
}
}....
I have a wrapper class for this AORule that's using GPARS activeObject to manage the state of DynamicRule rule reference like this, where the execute() method calls the async activeObject method to trigger the action on the internal rule reference
@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()
}
}
I have been trying to write some tests to show this is working to myself and it fails. I've tried to do two simple tests one for dynamicRule by itself which works - I setup the expectation and ask the Mock to call a closure and test the side effect of that closure on the mockres variable - all works ok as expected:
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"
}
In my second test I set up the Mock and set it on the aorule.rule instance and set the expectation for the mock, call the aorule.execute() method which invokes the activeExecute() active method, which calls my mock
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"
}
This fails - why closure is not run on the mock and the then: assertion test fails.
I've included part of the output trace here - you can see that aorule.execute() triggers the activeObject method, and I can see the log trace for the activeExec : rule dump looks like - however after that it all seems to stop, no extra trace output (I was expecting to see value of val for example), and the stub closure IS NOT called and mockres does not get set - test fails
...
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:
I have been trying to trace this, or watch in debugger to no avail. My stubs closure is not triggered - test fails.
I've just tried to watch in the debugger again and when I step past the line
def val = rule.dynamicExecution
the debugger jumps off into InternalActor.handleCurrentMessage() error handler somewhere in GPARs. Makes no difference if I change this to
def val = rule.getDynamicExecution() //try to get value via groovy auo method
I don't understand this. Perhaps I am doing something silly, but I can't write a test for my @ActiveMethod - activeExecute() to test code using Mocks (I've tried to do this via hand crafted classes, etc and cannot get the simulated tests to work.
How can I correct and run the tests properly? So far as I can tell - when I run the code in scripts ('live' tests) the code appears to do what I want it to do, etc but I can't make unit test to show that
Postscript: it's to do with ActiveMethod wrapping - not sure what
I created two dummy wrapper classes in the test script as follows - one without ActiveObject and one with ActiveObject support - both call the dynamicExecute() method on the embedded rule. AOWrappers is @ActiveMethod
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()
}
}
the two tests in same script file look this
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"
}
When I run the tests - the first one passes just fine, the second one fails and my clos is not called. What should I try next?
Clarification to question
I have tried and dumbed this down into single groovy test file - with classes being tested and the tests in the same file so that you can hopefully see what's going on
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"
}
}
The key point to note is the last test. I set the expectation on the Mock to run the output the closure, that will update the mockres variable, that I can assert has been set in the then: clause.
When you call the @ActiveMethod execute() the test will fail like this
...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)
which shows that mockres was not set by the closure call.
If you uncomment the aowrule.exec() line that follows - this then calls the delegated exec() call directly - not using the hidden actor which the ActiveMethod uses. When you run that next line the closure is called correctly and the mockres is set.
So the issue has something to do which trying to test that the mock works when its doing this through the hidden actor behind that @ActiveMothod.
I think in reality the code is doing what I expect if just run as a script (real use in code). However I seem unable to write the test that proves the expected behaviour when the ActiveMethod is being invoked.
Do I have to do something clever to write a spock test with Mocks for async code like GPARs - or is this something that should work but doesn't.