I run into an issue that, if I mock a class instance, then a static instance in the same class is not initialized correctly.
I have a real example of third party code with relevant to the question lines:
... public class Schema implements SchemaProvider { public static final Schema instance = new Schema(); private volatile Keyspaces distributedKeyspaces = Keyspaces.none(); private final Keyspaces localKeyspaces; private final boolean online; ... private Schema() { this.online = isDaemonInitialized(); this.localKeyspaces = (FORCE_LOAD_LOCAL_KEYSPACES || isDaemonInitialized() || isToolInitialized()) ? Keyspaces.of(SchemaKeyspace.metadata(), SystemKeyspace.metadata()) : Keyspaces.none(); } @VisibleForTesting public Schema(boolean online, Keyspaces localKeyspaces) { this.online = online; this.localKeyspaces = localKeyspaces; } ...
Then the project jar is used in another project where Schema
is mocked in a test:
Schema schema = Mockito.mock(Schema.class);
To my understanding this should not affect the static instance initialization, i.e., Schema.instance
. However, I run into an issue that the static instance is not initialized correctly and its properties are null
, i.e.:
assert Schema.instance.distributedKeyspaces == null; assert Schema.instance.localKeyspaces == null;
I’ve found that I can workaround the initialization issue in my test project by creating a dummy instance:
new Schema(false, Keyspaces.none()); Schema schema = Mockito.mock(Schema.class); // which gives me: assert Schema.instance.distributedKeyspaces != null; assert Schema.instance.localKeyspaces != null;
I have failed to find any information about this use case. So I would love to hear explanation of this behaviour and if it is expected or some uncommon usage, which runs in a kind of undefined behaviour. Is there a better way to workaround the issue? (preferably without changing the third-party library)
Java version: openjdk version “11.0.12” 2021-07-20
Mockito version: 3.5.0
Advertisement
Answer
It seems that Schema.class
loads the class Schema
but doesn’t initialize it. So when Mockito.mock
is called to create the proxy object, the class is initialized, but constructing Schema
for static final instance
is not done properly for some reason. I have empirical evidence, but no references to docs or code. I asked about in Mockito issue tracker
So the solution is to make sure that the class is loaded and initialized before calling Mockito. E.g., by calling Class.forName
:
Class.forName(Schema.class.getName()); // and then mock Schema schema = Mockito.mock(Schema.class);
or
Schema schema = Mockito.mock((Class<Schema>)Class.forName(Schema.class.getName()));