In short, somehow a mockito spy object got mixed up with original object.
Here is the class definition (pseudo code)
public class MyClass {
private String m_name = "origin name";
private final Dumper m_dumper = new Dumper();
public void setName(String name) {
m_name = name;
}
public void print() {
m_dumper.print();
}
public void dumpName() {
System.out.println(m_name);
}
private class Dumper {
public void print() {
dumpName();
}
}
}
so if we create the object and spy it:
MyClass originObject = new MyClass();
MyClass spyObject = Mockito.spy(originObject);
spyObject.setName("mock name");
spyObject.print(); //Here we are expecting it prints out "mock name" but actually "origin name"
I think the rootcause is that, When we spying original Object, spy object has its own member "m_dumper" but it still refers to original object.
Mockito creates a new instance and copies over all references from the original instance. Since Dumper
is a non-static nested class, it is bound to the instance of the enclosing class.
So basically you have the following reference chain:
spyObject -> spyObject.m_dumper -> spyObject.m_dumper.outerClass -> originObject
In other words, the m_dumper
instance is shared between spyObject
and originObject
. The fields of both instances contain identical values and reference the only dumper instance. You can easily verify with a debugger or by printing the identity hash code of the field references.
Straight from the docs:
Mockito does not delegate calls to the passed real instance, instead it actually creates a copy of it. So if you keep the real instance and interact with it, don't expect the spied to be aware of those interaction and their effect on real instance state. The corollary is that when an un-stubbed method is called on the spy but not on the real instance, you won't see any effects on the real instance.
You are keeping the real instance and interacting with it (through the non-static nested class Dumper
which keeps a reference to the real instance).