I use PowerMock to test a static method as mentioned its documentation.
As far as I see, there may be a bug, but I am not sure:
Static mocking broken for Mockito >= 2.26.1
...
I tried the workarounds mentioned on the following pages, however it does not fix the problem and some of them cannot be applicable as they are outdated.
NotAMockException when trying to verify a static method
verifyStatic get NotAMockExcption from mockito
However, I get "Argument passed to verify() is of type Class and is not a mock!" error. Here is the service method that I am testing and test method:
service:
// I want to test this method
public CommandDTO create(EmployeeRequest request) {
// ...
log();
return CommandDTO.builder().uuid(employee.getUuid()).build();
}
private void log() {
LoggingUtils.info("Created...");
}
test:
@RunWith(PowerMockRunner.class)
@PrepareForTest(LoggingUtils.class)
public class EMployeeServiceImplTest {
@Test
public void unit_test() {
// ...
PowerMockito.mockStatic(LoggingUtils.class);
employeeService.create(request);
PowerMockito.verifyStatic(LoggingUtils.class); // throws error
LoggingUtils.info(any());
}
}
Here are libraries and versions:
pom.xml:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.4.6</version>
<scope>test</scope>
</dependency>
CodePudding user response:
Since you asked in another comment, here's a pattern I've used in the past to prevent the need for static mocking:
Imagine we have a static method...
public class SomeClass {
public static String doSomething(int x) { ... }
}
Now imagine our code using it like this...
public class MyClass {
public void someMethod() {
String xyz = SomeClass.doSomething(123);
...
}
}
If we now needed to mock that SomeStaticClass.doSomething(123)
call, we need static mocking.
An alternative is a delegator:
public class MyDelegator {
public String doSomething(int x) {
return SomeClass.doSomething(x);
}
}
Now you can use that instead in your class:
public class MyClass {
private final MyDelegator myDelegator;
public MyClass(MyDelegator myDelegator) { this.myDelegator = myDelegator; }
public void someMethod() {
String xyz = myDelegator.doSomething(123);
...
}
}
And voila, you can simply mock the MyDelegator object instead of having to care that the original implementation was a static call.
Of course, this doesn't solve the underlying code smell, but it can help you to work around it in cases where you cannot refactor the static code itself.
CodePudding user response:
Let me suggest a different approach altogether because I see you already use Mockito version 3.4.6 .
Since Mockito >= 3.4.0
you are able to mock static methods directly. This means you can completely get rid of the PowerMock
dependency.
Also, AFAIK PowerMock does not support JUnit5. This solution will allow you to upgrade your JUnit test framework to version 5 without issues which I also highly recommend.
Static mocking using Mockito does require you to use the mockito-inline
dependency over mockito-core
but, because mockito-inline
depends on mockito-core
, Maven will also download the mockito-core
package. Please note that the Mockito documentation states that, at one point in the future, they might abolish this package when they pull all these
"experimental" features to the core package.
Next to that, you should also replace @RunWith(PowerMockRunner.class)
with @RunWith(MockitoJUnitRunner.class)
This is how you use it.
You can either define the mock per test method like so using a try-with-resources statement that automatically calls close
try (MockedStatic<Files> filesMock = Mockito.mockStatic(Files.class)) {
filesMock.when(() -> Files.copy(any(Path.class), any(OutputStream.class))).thenReturn(1L);
// Call logic
}
Or you can define it for all tests in a @Before
method because you use jUnit4.
private MockedStatic<Files> filesMock;
@Before
public void init() {
filesMock = org.mockito.Mockito.mockStatic(Files.class);
}
@After
public void teardown() {
filesMock.close();
}
@Test
void test() {
filesMock.when(() -> Files.copy(any(Path.class), any(OutputStream.class))).thenReturn(1L);
}
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#mockito-inline https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html#static_mocks