Home > Blockchain >  Mocking a class instance affects the static instance initialization
Mocking a class instance affects the static instance initialization

Time:05-25

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

CodePudding user response:

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()));
  • Related