import groovy.mock.interceptor.MockForinterface Inner {}class Tst {class InnerImpl implements Inner {// Variant 1: Having multiple constructors prevents the "as" operator to work properlyInnerImpl () {println "Inner: Real Inner Constructor called"}InnerImpl (boolean second) {println "Inner: Second Real Inner Constructor called"}}Tst () {println "Tst: Constructor called"}Tst (boolean second) {println "Tst: Second Constructor called"}void tst () {println "Constructing Inner: "Inner inner = new InnerImpl(true)}}Tst tst = new Tst()tst.tst()// This works: Coercing the array to Tst - though Tst has two constructorsdef dummyTst = [:] as Tstprintln "Successfully created the dummy Tst: $dummyTst"// Now the problem start!// Variant 1def dummyInner = [toString:{"DummyInner1"}] as Tst.InnerImpl// Variant 2 (also comment out the InnerImpl default constructor)// def dummyInner = [toString:{"DummyInner2"}] as Inner// Variant 3// def dummyInner = new Tst.InnerImpl()// Variant 4: Works! But it is very strange to have the additional argument// def dummyInner = new Tst.InnerImpl(tst)println "DummyInner is ${dummyInner}"MockFor mockInner = new MockFor (Tst.InnerImpl, true)mockInner.demand.with {InnerImpl {Tst ignore, boolean second->println "Mock Constructor called"println "Returning $dummyInner"dummyInner}}mockInner.use {Tst tstMocked = new Tst()tstMocked.tst()}
Caught: org.codehaus.groovy.runtime.typehandling.GroovyCastException: Error casting map to Tst$InnerImpl, Reason: nullorg.codehaus.groovy.runtime.typehandling.GroovyCastException: Error casting map to Tst$InnerImpl, Reason: nullat mockfor2.run(mockfor2.groovy:39)
Caught: java.lang.ClassCastException: com.sun.proxy.$Proxy5 cannot be cast to groovy.lang.GroovyObjectjava.lang.ClassCastException: com.sun.proxy.$Proxy5 cannot be cast to groovy.lang.GroovyObjectat Tst.tst(mockfor2.groovy:26)at Tst$tst.call(Unknown Source)at mockfor2$_run_closure3.doCall(mockfor2.groovy:59)at mockfor2$_run_closure3.doCall(mockfor2.groovy)at mockfor2.run(mockfor2.groovy:57)
Caught: org.codehaus.groovy.runtime.metaclass.MethodSelectionException: Could not find which method <init>() to invoke from this list:public Tst$InnerImpl#<init>(Tst)public Tst$InnerImpl#<init>(Tst, boolean)org.codehaus.groovy.runtime.metaclass.MethodSelectionException: Could not find which method <init>() to invoke from this list:public Tst$InnerImpl#<init>(Tst)public Tst$InnerImpl#<init>(Tst, boolean)at mockfor2.run(mockfor2.groovy:43)
On 21 August 2014 07:31, Aschemann Gerd <ge...@aschemann.net> wrote:In "reality" my problem code is much more complex ... tried to reduce it to a small sample which shows the critical points
On 20.08.2014, at 14:56, Dinko Srkoč <dinko...@gmail.com> wrote:Inner classes cannot be constructed without their enclosing class
being constructed first. For that, one would need a static nested
class.
Thanks, Dinko, that at least explains why Variant 4 works, but V3 not: The call to the constructor of the inner class needs a reference to an object of the outer class (since in more advanced cases, the inner class might use attributes/methods of the outer class, or the respective object, to be more precise).
Yes, and I believe this is where Groovy differs from Java - it adds a
reference to the enclosing class as a first parameter to its
constructors.
And it might also be a hint why V1 does not work: Does the coerced object also need a reference to the outer class? In that case I would like to have a better error message, not "Reason: null" ... Should I file a bug for groovy?
To coerce a map to given object Groovy must somehow construct that
object, so yes, I would say that the coerced object also needs a
reference to the outer class during construction.
If `InnerImpl` was a static class, the coercion would probably work.
...class Tst {static class InnerImpl implements Inner {...
MockFor mockInner = new MockFor (Tst.InnerImpl, true)mockInner.demand.with {
InnerImpl {boolean second->
println "Mock Constructor called"println "Returning $dummyInner"dummyInner}}
...