如何将静态方法重载/覆盖为 return 另一个值?

How to overload / override static method to return an other value?

基本上我正在尝试模拟静态 class 进行单元测试。

示例:

class Library {
    public static boolean isActivated() {
        return false;
    }
}

class Test {
    public static void main(String[] args) {
        // some magic
        
        if (Library.isActivated()) {
            System.out.println("'some magic' works!");
        } else {
            System.out.println("fail");
        }
    }
}

some magic 需要什么才能使 Library.isActivated() return true.

是否有一些特殊的方法可以在不更改 Library 的源代码的情况下执行此操作?

据我所知,这在 java 中是不可能的,但我对 Reflection 不是很熟悉,我认为这可能是可能的。

静态方法不是虚拟的,因此不能被覆盖。重载不会满足您的目的,因为它涉及具有相同名称但参数类型不同的方法,并且在任何情况下,您都需要修改 class Library 以添加重载。 Java 语言没有为不同的 class 提供对现有 Library class.

施加重载的方法

总体而言,这反映了依赖静态方法和非最终静态数据的更广泛问题。

Is there some special way to do this without changing the source code of Library?

As far as I know this is not possible in java but I'm not very familiar with Reflection I thought this might be possible with it.

反射也不行。您需要做的是加载与您显示的版本不同的 class 版本。这可以通过

实现
  • 精心管理class路径
  • Library
  • 不同版本的即时合成
  • ClassLoaders
  • 一起玩游戏

如果你想限制替换的范围 class —— 这样你的替换就不会在 other 测试中使用 —— 那么你可能需要行使 ClassLoader 选项,尽管这不一定排除其他两个。这很重要。

但是,您可能能够找到一个可以为您处理所有这些的模拟库。

绝对不能覆盖静态方法。但是,您可以通过大量工作来模拟静态方法。通常,模拟静态方法不是一个好兆头。幸运的是,如果你需要的话,有一些图书馆可以做到这一点。

Mockito 添加了对此的支持,Baeldung 有一个很好的教程演示了如何做到这一点:https://www.baeldung.com/mockito-mock-static-methods#mocking-a-no-argument-static-method

基于教程:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.2.0</version>
    <scope>test</scope>
</dependency>
@Test
void mockLibraryIsActivated() {

    // Before mocking the static method is going to return false
    assertFalse(Library.isActivated());

    // Using MockedStatic to mock the method
    try (MockedStatic<Library> utilities = Mockito.mockStatic(Library.class)) {
        utilities.when(Library::isActivated).thenReturn(true);

        // Perform your test, the mock only works within the try block
        assertTrue(Library.isActivated());
    }

    // Mock is no longer active
    assertFalse(Library.isActivated());
}

对此的一个关键限制是模拟仅适用于它定义的代码块。因此,如果您有另一个单元测试,您也必须在该单元测试中模拟它。

如果你真的想钻进兔子洞,你可以看看 MockitoCore.mockStatic 在 GitHub 仓库中是如何实现的:https://github.com/mockito/mockito/blob/main/src/main/java/org/mockito/internal/MockitoCore.java.