Search code examples
javaspringspring-bootunit-testingmockito

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


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<MyStaticClass> 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):

<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>

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


Solution

  • 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("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));
            }
        }
    }
    

    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.