Mockito使用`Mockito.mockStatic()`来模拟静态void方法。

huangapple go评论83阅读模式
英文:

Mockito Mock a static void method with Mockito.mockStatic()

问题

我正在使用Spring Boot,在我的一个单元测试中,我需要模拟Files.delete(somePath)函数。这是一个静态的void方法。

我知道使用Mockito可以模拟void方法:

doNothing().when(MyClass.class).myVoidMethod()

自从2020年7月10日以来,也可以模拟静态方法:

try (MockedStatic<MyStaticClass> mockedStaticClass = Mockito.mockStatic(MyStaticClass.class)) {
    mockedStaticClass.when(MyStaticClass::giveMeANumber).thenReturn(1L);
    assertThat(MyStaticClass.giveMeANumber()).isEqualTo(1L);
}

但我无法成功模拟静态void方法,比如Files.delete(somePath)

这是我的pom.xml文件(仅包含与测试相关的依赖项):

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>3.5.15</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.5.15</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-junit-jupiter</artifactId>
    <version>3.5.15</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <version>2.2.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.security</groupId>
    <artifactId>spring-security-test</artifactId>
    <scope>test</scope>
</dependency>

是否有一种方法可以在不使用PowerMockito的情况下模拟静态void方法?如果可能的话,正确的语法是什么?

英文:

I'm using Spring Boot and in one of my unit test, I need to mock the Files.delete(somePath) function. Which is a static void method.

I know that with Mockito it is possible to mock void method:

doNothing().when(MyClass.class).myVoidMethod()

And since July 10th 2020, it is possible to mock static method:

try (MockedStatic&lt;MyStaticClass&gt; mockedStaticClass = Mockito.mockStatic(MyStaticClass.class)) {
    mockedStaticClass.when(MyStaticClass::giveMeANumber).thenReturn(1L);
    assertThat(MyStaticClass.giveMeANumber()).isEqualTo(1L);
  }

But I can't manage to mock a static void mehtod such as Files.delete(somePath).

This is my pom.xml file (only test related dependencies):

&lt;dependency&gt;
	&lt;groupId&gt;org.mockito&lt;/groupId&gt;
	&lt;artifactId&gt;mockito-inline&lt;/artifactId&gt;
	&lt;version&gt;3.5.15&lt;/version&gt;
	&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
	&lt;groupId&gt;org.mockito&lt;/groupId&gt;
	&lt;artifactId&gt;mockito-core&lt;/artifactId&gt;
	&lt;version&gt;3.5.15&lt;/version&gt;
	&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.mockito&lt;/groupId&gt;
    &lt;artifactId&gt;mockito-junit-jupiter&lt;/artifactId&gt;
    &lt;version&gt;3.5.15&lt;/version&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;
    &lt;artifactId&gt;spring-boot-starter-test&lt;/artifactId&gt;
    &lt;scope&gt;test&lt;/scope&gt;
    &lt;version&gt;2.2.6.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework.security&lt;/groupId&gt;
    &lt;artifactId&gt;spring-security-test&lt;/artifactId&gt;
    &lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;

Is there a way to mock static void methods without using PowerMockito ?
If it is possible, what is the correct syntax to do so ?

答案1

得分: 18

通常情况下,模拟静态调用是最后的手段,不应作为默认方法。

例如,为了测试与文件系统交互的代码,有更好的方法。例如,根据junit版本,可以使用TemporaryFolder规则或者@TempDir注解

还请注意,Mockito.mockStatic可能会显著减慢您的测试速度(请参阅下面的注意事项)。

在上述警告之后,以下是一个示例代码片段,展示了如何测试文件是否被删除。

class FileRemover {
    public static void deleteFile(Path filePath) throws IOException {
        Files.delete(filePath);
    }
}

class FileRemoverTest {

    @TempDir
    Path directory;

    @Test
    void fileIsRemovedWithTemporaryDirectory() throws IOException {
        Path fileToDelete = directory.resolve("fileToDelete");
        Files.createFile(fileToDelete);

        FileRemover.deleteFile(fileToDelete);

        assertFalse(Files.exists(fileToDelete));
    }

    @Test
    void fileIsRemovedWithMockStatic() throws IOException {
        Path fileToDelete = Paths.get("fileToDelete");
        try (MockedStatic<Files> removerMock = Mockito.mockStatic(Files.class)) {
            removerMock.when(() -> Files.delete(fileToDelete)).thenAnswer((Answer<Void>) invocation -> null);
            // alternatively
            // removerMock.when(() -> Files.delete(fileToDelete)).thenAnswer(Answers.RETURNS_DEFAULTS);

            FileRemover.deleteFile(fileToDelete);

            removerMock.verify(() -> Files.delete(fileToDelete));
        }
    }
}

注意:

  1. Mockito.mockStatic在Mockito 3.4及以上版本可用,请检查您使用的版本是否正确。

  2. 代码片段故意展示了两种方法:@TempDirMockito.mockStatic。当同时运行这两个测试时,您会注意到Mockito.mockStatic的速度要慢得多。例如,在我的系统上,使用Mockito.mockStatic运行时间大约为 900 毫秒,而使用@TempDir则为 10 毫秒。

英文:

In general mocking static calls is the last resort, that is not supposed to be used as default approach.

For example, for testing the code, that works with file system, there are better means. E.g. depending on the junit version either use TemporaryFolder rule or @TempDir annotation.

Also, please note, that Mockito.mockStatic might significantly slow down your tests (e.g. look at the notes below).

Having said the caution above, find the snippet below, that shows how to test, that file got removed.

class FileRemover {
    public static void deleteFile(Path filePath) throws IOException {
        Files.delete(filePath);
    }
}

class FileRemoverTest {

    @TempDir
    Path directory;

    @Test
    void fileIsRemovedWithTemporaryDirectory() throws IOException {
        Path fileToDelete = directory.resolve(&quot;fileToDelete&quot;);
        Files.createFile(fileToDelete);

        FileRemover.deleteFile(fileToDelete);

        assertFalse(Files.exists(fileToDelete));
    }

    @Test
    void fileIsRemovedWithMockStatic() throws IOException {
        Path fileToDelete = Paths.get(&quot;fileToDelete&quot;);
        try (MockedStatic&lt;Files&gt; removerMock = Mockito.mockStatic(Files.class)) {
            removerMock.when(() -&gt; Files.delete(fileToDelete)).thenAnswer((Answer&lt;Void&gt;) invocation -&gt; null);
            // alternatively
            // removerMock.when(() -&gt; Files.delete(fileToDelete)).thenAnswer(Answers.RETURNS_DEFAULTS);

            FileRemover.deleteFile(fileToDelete);

            removerMock.verify(() -&gt; Files.delete(fileToDelete));
        }
    }
}

Notes:

  1. Mockito.mockStatic is available in Mockito 3.4 and above, so check you're using correct version.

  2. The snippet deliberatly shows two approaches: @TempDir and Mockito.mockStatic. When run both tests you'll notice that Mockito.mockStatic is much slower. E.g. on my system test with Mockito.mockStatic runs around 900 msec vs 10 msec for @TempDir.

答案2

得分: 0

使用mockStatic,然后在返回的实例中指定模拟对象。

try (MockedStatic<Files> mockFiles = Mockito.mockStatic(Files.class)) {
    mockFiles.when(() -> Files.delete(anyString()))
             .then(invocationOnMock -> null);
}
英文:

Use mockStatic and then specify the mock in the returned instance.

try (MockedStatic&lt;Files&gt; mockFiles = Mockito.mockStatic(Files.class)) {
    mockFiles.when(() -&gt; Files.delete(anyString()))
                     .then(invocationOnMock -&gt; null);
}

huangapple
  • 本文由 发表于 2020年10月21日 20:39:13
  • 转载请务必保留本文链接:https://go.coder-hub.com/64463733.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定