'How do I mock a static void method (not with doNothing or invocation -> null) using Mockito.mockedStatic?

I have the following classes

class FileDownloader {
    public static void downloadFile(String repoUrl, String filename) throws IOException {
        // Downloads file from internet
    }
}

class FileDownloaderAndProcessor {
    public static void downloadAndProcessFile() throws IOException {
        //run some processing to figure out what to download
        FileDownloader.downloadFile(a, b);
        //run various processing on downloaded file
    }
}

I used to mock FileDownloader with PowerMockito.doAnswer, where I could basically mock the downloadFiles function by copying a local file from one place to another to simulate the download, then run various tests on the postproccessing

PowerMockito.doAnswer(invocation -> {
//copy from local location x to y depending on arguments
}.when(FileDownloader.class, "downloadFile").etc etc

Due to a recent spring upgrade, we can no longer use powermock and are migrating to MockedStatic. However, I don't see the doAnswer functionality in MockedStatic. I really don't want the tests to require an internet connection to run. How do I mock this void method?

Mockito version: org.mockito:mockito-core:jar:4.0.0 org.mockito:mockito-inline:jar:4.0.0



Solution 1:[1]

You can try when...thenAnswer... on MockedStatic provided by Mockito.

try (MockedStatic<FileDownloader> mockedFileDownloader = Mockito.mockStatic(FileDownloader.class)) {
    mockedFileDownloader.when(() -> FileDownloader.downloadFile(repoUrl, filename))
        .thenAnswer(invocation -> {
            // copy from local location x to y depending on arguments
            return null;
        });
}

Solution 2:[2]

Since all your static classes seem to have only one method you can simply mock the entire class.

Mockito.mockStatic(FileDownloader.class, invocationOnMock -> {
    System.out.println("Do Something that simulates the download");
    return null;
});

FileDownloader.downloadFile("someRepo", "someFile");

EDIT:

Only mocking a single Method in the class would also be possible with a hacky workaround.

    MockedStatic<FileDownloader> your_mocked_call = mockStatic(FileDownloader.class, invoca -> {
        if (invoca.getMethod().getName().equals("downloadFile")) {
            // custom code here
            System.out.println("aaaaaaaaaaaaaaaaaaa");
        }
        return null;
    });

I would however suggest to use the solution provided by Kai-Sheng Yang https://stackoverflow.com/a/72125083/10254476. It is the indended way by mockito. My original simple approach is only simple as long as the entire class can be mocked.

** always remember to call your_mocked_call.close(); otherwise the static mock stays open even after the testClass is finished.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2