Is there a way with Mockito to run a stubbed metho

2019-05-26 07:04发布

问题:

I'm writing junit tests using Mockito for a code that has been implemented by someone else. Simplifying:

  • There is an Outer class and an Inner class
  • The Outer class keeps an instance of the Inner class.
  • The Inner class uses a method of the Outer class.

The simplified code could looks like this:

public class Outer {
    private Inner inner;

    public Outer(){
         inner=new Inner();
    }

    public Inner getInner(){
        return inner;
    }

    public String getOuterName(){
        return "outer";
    }

    public String getOuterNiceName(){
        return "name="+getOuterName();
    }

    public class Inner {
        public String getInnerName(){
            return getOuterName()+"-inner";     
        }
    }
}

I want to stub the method getOuterName() of the Outer class to return a value convenient for my test. To do that I'm "spying" on the object:

Outer outer=new Outer();
Outer spyOuter=spy(outer);
doReturn("outerspied").when(spyOuter).getOuterName();

Now, if I call:

spyOuter.getObjectName(); 
spyOuter.getOuterNiceName(); 

The stubbed method is invoked in both calls, and I get the string: "outerspied" and "name=outerspied"

However if I call:

spyOuter.getInner().getInnerName(); 

In this case the stubbed method is not called, but the original one. I expected "outerspied-inner", but got "outer-inner"

Summarising, in the spied outer object the reference "this" points to the spied instance. But for the inner object, the reference to "Outer.this" points to a "non spied" instance.

I don't know if I'm doing something wrong, it is a bug, or it is working as designed; question is: is there a workaround?

Many thanks in advance

回答1:

I spy only surrounds the spied object with a proxy. It does not have any mechanism to replace calls within the spied object. So even if a method is stubbed via the spy, if the spied object calls that method directly the spied object's method is invoked not the spy's stub. This is basically exactly what is going on with your Inner / Outter. If Inner were not using the this instance of Outter you might be able to use reflection to replace Inner's reference to Outter. But since Inner is an instance inner class (instead of static) and since invocation of getOutterName is using the implicit Outter.this you don't have a mechanism to override this.