模拟 class 实例会影响静态实例初始化

Mocking a class instance affects the static instance initialization

我 运行 遇到一个问题,如果我模拟一个 class 实例,那么同一个 class 中的静态实例不会正确初始化。

我有一个与问题行相关的第三方代码的真实示例:

...
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;
    }
    ...

然后在另一个项目中使用项目 jar,其中 Schema 在测试中被模拟:

Schema schema = Mockito.mock(Schema.class);

根据我的理解,这应该不会影响静态实例初始化,即 Schema.instance。但是,我 运行 遇到一个问题,即静态实例未正确初始化且其属性为 null,即:

assert Schema.instance.distributedKeyspaces == null;
assert Schema.instance.localKeyspaces == null;

我发现我可以通过创建虚拟实例来解决测试项目中的初始化问题:

new Schema(false, Keyspaces.none());
Schema schema = Mockito.mock(Schema.class);
// which gives me:
assert Schema.instance.distributedKeyspaces != null;
assert Schema.instance.localKeyspaces != null;

我未能找到有关此用例的任何信息。所以我很想听听对这种行为的解释,如果它是预期的或一些不常见的用法,运行s 是一种未定义的行为。有没有更好的方法来解决这个问题? (最好不改第三方库)

Java版本:openjdk版本“11.0.12”2021-07-20

Mockito 版本:3.5.0

似乎 Schema.class 加载了 class Schema 但没有初始化它。所以当调用Mockito.mock创建代理对象时,class被初始化,但是为static finalinstance构造Schema由于某些原因没有正确完成。我有经验证据,但没有参考文档或代码。我在 Mockito issue tracker

中询问

所以解决方案是确保在调用 Mockito 之前加载并初始化 class。例如,通过调用 Class.forName:

Class.forName(Schema.class.getName());
// and then mock
Schema schema = Mockito.mock(Schema.class);

Schema schema = Mockito.mock((Class<Schema>)Class.forName(Schema.class.getName()));