Mocking Static Methods With Mockito – 用Mockito嘲弄静态方法

最后修改: 2021年 3月 26日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

When writing tests, we’ll often encounter a situation where we need to mock a static method. Previous to version 3.4.0 of Mockito, it wasn’t possible to mock static methods directly — only with the help of PowerMockito.

在编写测试时,我们经常会遇到需要对静态方法进行模拟的情况。在Mockito的3.4.0版本之前,我们不可能直接模拟静态方法–只能借助PowerMockito的帮助。

In this tutorial, we’ll take a look at how we can now mock static methods using the latest version of Mockito.

在本教程中,我们将看看我们现在如何使用最新版本的Mockito来模拟静态方法。

To learn more about testing with Mockito, check out our comprehensive Mockito series.

要了解有关使用Mockito测试的更多信息,请查看我们全面的Mockito系列

2. A Simple Static Utility Class

2.一个简单的静态实用类

The focus of our tests will be a simple static utility class:

我们测试的重点将是一个简单的静态实用类。

public class StaticUtils {

    private StaticUtils() {}

    public static List<Integer> range(int start, int end) {
        return IntStream.range(start, end)
          .boxed()
          .collect(Collectors.toList());
    }

    public static String name() {
        return "Baeldung";
    }
}

For demonstration purposes, we have one method with some arguments and another one that simply returns a String.

为了演示,我们有一个带有一些参数的方法和另一个简单返回String的方法。

3. Configuring Mockito for Static Methods

3.为静态方法配置Mockito

Before we can use Mockito for mocking static methods, we need to configure it to activate inline MockMaker.

在我们使用Mockito来模拟静态方法之前,我们需要配置它来激活内联MockMaker

We need to add a text file to the project’s src/test/resources/mockito-extensions directory named org.mockito.plugins.MockMaker and add a single line of text:

我们需要在项目的src/test/resources/mockito-extensions目录下添加一个名为org.mockito.plugins.MockMaker的文本文件并添加一行文字。

mock-maker-inline

4. A Quick Word on Testing Static Methods

4.关于测试静态方法的简要说明

Generally speaking, some might say that when writing clean object-orientated code, we shouldn’t need to mock static classes. This could typically hint at a design issue or code smell in our application.

一般来说,有人可能会说,在编写干净的面向对象的代码时,我们不应该需要模拟静态类。这通常会暗示我们的应用程序中存在设计问题或代码味道

Why? First, a class depending on a static method has tight coupling, and second, it nearly always leads to code that is difficult to test. Ideally, a class should not be responsible for obtaining its dependencies, and if possible, they should be externally injected.

为什么呢?首先,一个依赖于静态方法的类具有紧密的耦合性,其次,它几乎总是导致难以测试的代码。理想情况下,一个类不应该负责获得它的依赖关系,如果可能的话,它们应该是外部注入的。

So, it’s always worth investigating if we can refactor our code to make it more testable. Of course, this is not always possible, and sometimes we need to mock static methods.

因此,我们总是值得研究是否可以重构我们的代码以使其更具可测试性。当然,这并不总是可能的,有时我们需要对静态方法进行模拟。

5. Mocking a No Argument Static Method

5.嘲弄一个没有参数的静态方法

Let’s go ahead and see how we can mock the name method from our StaticUtils class:

让我们继续,看看我们如何从StaticUtils类中模拟name方法。

@Test
void givenStaticMethodWithNoArgs_whenMocked_thenReturnsMockSuccessfully() {
    assertThat(StaticUtils.name()).isEqualTo("Baeldung");

    try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {
        utilities.when(StaticUtils::name).thenReturn("Eugen");
        assertThat(StaticUtils.name()).isEqualTo("Eugen");
    }

    assertThat(StaticUtils.name()).isEqualTo("Baeldung");
}

As previously mentioned, since Mockito 3.4.0, we can use the Mockito.mockStatic(Class<T> classToMock) method to mock invocations to static method calls. This method returns a MockedStatic object for our type, which is a scoped mock object.

如前所述,从Mockito 3.4.0开始,我们可以使用Mockito.mockStatic(Class<T> classToMock) 方法来模拟静态方法调用的援引。该方法为我们的类型返回一个MockedStatic对象,它是一个范围内的模拟对象。

Therefore, in our unit test above, the utilities variable represents a mock with a thread-local explicit scope. It’s important to note that scoped mocks must be closed by the entity that activates the mock. This is why we define our mock within a try-with-resources construct so that the mock is closed automatically when we finish with our scoped block.

因此,在我们上面的单元测试中,utilities变量代表一个具有线程本地显式范围的模拟。需要注意的是,范围内的模拟必须由激活模拟的实体来关闭。这就是为什么我们在try-with-resources结构中定义我们的模拟,以便在我们完成范围内的块时自动关闭模拟。

This is a particularly nice feature since it assures that our static mock remains temporary. As we know, if we’re playing around with static method calls during our test runs, this will likely lead to adverse effects in our test results due to the concurrent and sequential nature of running tests.

这是一个特别好的功能,因为它保证了我们的静态模拟保持临时性。正如我们所知,如果我们在测试运行期间玩弄静态方法调用,由于运行测试的并发性和顺序性,这很可能会导致我们的测试结果出现不良影响。

On top of this, another nice side effect is that our tests will still run quite fast since Mockito doesn’t need to replace the classloader for every test.

除此之外,另一个很好的副作用是,我们的测试仍然会运行得相当快,因为Mockito不需要为每个测试更换类加载器。

In our example, we reiterate this point by checking, before and after our scoped block, that our static method name returns a real value.

在我们的例子中,我们重申了这一点,在我们的作用域块之前和之后,检查我们的静态方法name是否返回一个真实的值。

6. Mocking a Static Method With Arguments

6.嘲弄一个有参数的静态方法

Now let’s see another common use case when we need to mock a method that has arguments:

现在让我们看看另一个常见的用例,当我们需要模拟一个有参数的方法时。

@Test
void givenStaticMethodWithArgs_whenMocked_thenReturnsMockSuccessfully() {
    assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);

    try (MockedStatic<StaticUtils> utilities = Mockito.mockStatic(StaticUtils.class)) {
        utilities.when(() -> StaticUtils.range(2, 6))
          .thenReturn(Arrays.asList(10, 11, 12));

        assertThat(StaticUtils.range(2, 6)).containsExactly(10, 11, 12);
    }

    assertThat(StaticUtils.range(2, 6)).containsExactly(2, 3, 4, 5);
}

Here, we follow the same approach, but this time we use a lambda expression inside our when clause where we specify the method along with any arguments that we want to mock. Pretty straightforward!

在这里,我们遵循同样的方法,但这次我们在when子句中使用lambda表达式,我们指定了方法以及我们想要模拟的任何参数。

7. Conclusion

7.结语

In this quick article, we’ve seen a couple of examples of how we can use Mockito to mock static methods. To sum up, Mockito provides a graceful solution using a narrower scope for mocked static objects via one small lambda.

在这篇快速的文章中,我们已经看到了几个例子,说明我们如何使用Mockito来模拟静态方法。总而言之,Mockito提供了一个优雅的解决方案,通过一个小的lambda为被模拟的静态对象提供了一个更窄的范围。

As always, the full source code of the article is available over on GitHub.

一如既往,该文章的完整源代码可在GitHub上获得