JUnit和Powermock:本地库已经在另一个类加载器中加载

4

我有一些测试类需要验证是否调用了GLFW函数。但是当我想在IntelliJ中执行所有测试时,就会出现以下错误:

Native Library lwjgl.dll already loaded in another classloader

我使用Powermock来验证静态方法是否被调用:

@RunWith(PowerMockRunner.class)
@PrepareForTest({GLFW.class})
public class GlfwWindowImplTest {
    // ...
    @Test
    public void update_swapsBufferAndPollsEvents() {
        GlfwWindowImpl target = new GlfwWindowImpl(1L);
        mockStatic(GLFW.class);

        target.update();

        verifyStatic();
        GLFW.glfwSwapBuffers(1L);
        verifyStatic();
        GLFW.glfwPollEvents();
    }
}

@RunWith(PowerMockRunner.class)
@PrepareForTest({GLFW.class})
public class GlfwWindowSystemImplTest {
    // ...
    @Test(expected = GlfwInitializeException.class)
    public void initialize_throwsExceptionIfGlfwInitFails() {
        GlfwWindowSystemImpl target = new GlfwWindowSystemImpl();
        mockStatic(GLFW.class);
        when(GLFW.glfwInit()).thenReturn(GL_FALSE);

        target.initialize();
    }
}

@PrepareForTest({GLFW.class}) 将类进行修改并加载到新的类加载器中。 - talex
@Exhauzt,你解决了这个问题吗?我不明白下面的解决方案告诉你要做什么。你的原始代码似乎已经有了@PrepareForTest({GLFW.class})...那么你需要做什么不同的事情呢?谢谢。 - Nova
不,实际上我没有找到解决方案,但是不久之后我又开始做另一个项目了...所以我没有花太多时间在这个主题上寻找解决方案。而且我也不理解那个解决方案。 - danielspaniol
3个回答

4
可能有点晚回答这个帖子,但我遇到了类似的问题,并且我能够使用PowerMock解决它。 PowerMock具有@SuppressStaticInitializationFor,应该可以帮助您克服此问题。下面链接中的Suppress static initializer部分有一个很好的示例,介绍了如何做到这一点:

https://github.com/powermock/powermock/wiki/Suppress-Unwanted-Behavior

基本上,在您的情况下,可能有一个类正在调用System.loadLibrary("lwjgl")。您需要找到该类。例如:
public class SomeClass {
    ...
    static {
        System.loadLibrary ("lwjgl");
    }
    ...
}

然后在你的测试类中使用@SuppressStaticInitializationFor和类的完全限定名:

@SuppressStaticInitializationFor("com.example.SomeClass")

如果进行loadLibrary调用的类是内部类,则需要添加$InnerClass以完全限定内部类。示例:
public class SomeClass {
    ...
    public static class SomeInnerClass {
        static {
            System.loadLibrary ("lwjgl");
        }
    }
    ...
}

那么你需要使用:

@SuppressStaticInitializationFor("com.example.SomeClass$SomeInnerClass")

永远不要太晚了! - Gyorgy Szekely
@RichardT 很好有一个示例代码片段,展示如何使用 @SuppressStaticInitializationFor - Prakhar Asthana

1

@PrepareForTest({GLFW.class})会加载本地库lwjgl.dll的ClassLoader,该库已经在ClassLoader中加载。

您可以使用@PowerMockIgnore来推迟将名称提供给value()的类的加载到系统ClassLoader中。

例如,假设您想推迟加载org.myproject包及其所有子包中的所有类,但仍要为"MyClass"准备测试。那么您可以这样做:


 @PowerMockIgnore("org.myproject.*")
 @PrepareForTest(MyClass.class)
 @RunWith(PowerMockRunner.class)
 public class MyTest {
 ...
 }

JavaDoc参考文档


0

@PrepareForTest({GLFW.class}) 创建新的 ClassLoader 并在其中加载修改后的类。我怀疑 GLFW 在其静态初始化器部分中加载了 lwjgl 库。

您可以尝试卸载库,但我不知道这可能会导致什么后果。

我建议包装 GLFW 并在您的应用程序中使用该包装器。


1
这个回答非常令人困惑。你是说解决方案是 @PrepareForTest({GLFW.class}) 吗? 这就是他原来的代码已经有的。你所说的“包装GLFW并在你的应用程序中使用该包装器”是什么样子的?请澄清一下 :) - Nova
1
@PrepareForTest({GLFW.class}) 并非解决之道,反而是问题的原因。它会导致类的重新加载,而重新加载类会执行静态部分,进而导致 lwjgl.dll 库的第二次加载。 - talex

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接