PowerMockRunner does not apply JUnit ClassRules - java

I need to mock a static method of a class and use that mocked method in my test. Right now seems I can only use PowerMock to do that.
I annotate the class with #RunWith(PowerMockRunner.class), and #PrepareForTest with the appropriate class.
In my test I have a #ClassRule, but when running the tests, the rule is not applied properly.
What can i do ?
RunWith(PowerMockRunner.class)
#PowerMockIgnore({
"javax.xml.*",
"org.xml.*",
"org.w3c.*",
"javax.management.*"
})
#PrepareForTest(Request.class)
public class RoleTest {
#ClassRule
public static HibernateSessionRule sessionRule = new HibernateSessionRule(); // this Rule doesnt applied

Another way of working around this problem is to use the org.powermock.modules.junit4.PowerMockRunnerDelegate annotation:
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(BlockJUnit4ClassRunner.class)
#PowerMockIgnore({
"javax.xml.*",
"org.xml.*",
"org.w3c.*",
"javax.management.*"
})
#PrepareForTest(Request.class)
public class RoleTest {
#ClassRule
public static HibernateSessionRule sessionRule = new HibernateSessionRule(); // this Rule now applied

I looked into the PowerMock code. It looks like PowerMockRunner does not support #ClassRule. You can try to use the HibernateSessionRule as a #Rule instead of a #ClassRule.
#PrepareForTest(Request.class)
public class RoleTest {
#Rule
public HibernateSessionRule sessionRule = new HibernateSessionRule();

I found another solution only valid with PowerMock 1.4 or major. I added these dependencies to my pom.xml
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-classloading-xstream</artifactId>
<version>2.0.2</version>
<scope>test</scope>
</dependency>
and changed my code removing #RunWith annotation and using a simple JUnit #Rule
#PrepareForTest(X.class);
public class MyTest {
#Rule
PowerMockRule rule = new PowerMockRule();
// Tests goes here
...
}
for more information visit PowerMock's documentation

Related

Failed to release mocks - Mockito

I am trying to upgrade from Mockito version 1.0.19 to 4.0.0 and using Junit 5, since I am unable to mock static in older version of mockito. I am getting "Failed to Release mocks" error..
Please let me know , what all needs to be taken care while migrating.
public class RefreshTableSchedulerTest {
#Mock
ConfigRepository configRepository;
#InjectMocks
RandomScheduler randomScheduler;
#BeforeEach
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
setReflectionUtils(randomScheduler);
}
#Test
public void testRefreshTableWithOutDelay() {
// our trestcases
}
RandomScheduler
#Configuration
#EnableScheduling
public class RandomScheduler {
#Scheduled(fixedDelayString = "${schedule.refresh.table.job.in.ms:1800000}")
public void execute() {
//fetch data from table A
//inserts data to Table B using timestamps got from Table A
//updates timestamp of Table A
}
Failed to Release mocks can happen when your dependencies are not aligned. Since you are using Spring Boot, make sure to not bump major Mockito version, but rather use spring-boot-starter-test and correct version of Spring Boot parent that will bring aligned set of dependencies including Mockito.
If you are trying to mock static class using mockito, you will need the following dependency first. If you try to mock static class without this dependency it will throw that error
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
Then mock the static class by using
MockedStatic<YourStaticClass> yourStaticClass = mockStatic(YourStaticClass.class)
Also make sure whether you are using the correct dependencies in your pom
Try adding #ExtendWith(MockitoExtension.class) on top of the test class and removing MockitoAnnotations.initMocks(this); from setUp method.

How to mock a record with Mockito

I'm trying to mock a record class
#Test
public void testRecord() {
record Rec(){}
Mockito.mock(Rec.class);
}
But it gives the error
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class Rec
Mockito cannot mock/spy because :
- final class
at ...
Which makes sense of course.
As the error message suggests, you cannot mock final classes with the default Mockito.
But the community came up with mockito-inline, an extension bringing experimental features such as mocking final classes and methods or static methods.
Just add this in your pom.xml and use Mockito normally.
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<scope>test</scope>
</dependency>
and for Gradle:
testImplementation 'org.mockito:mockito-inline:4.11.0'

How to mock static method in Junit 5? [duplicate]

I want to mock a static method in JUnit 5. But unfortunately, JUnit 5 doesn’t support Mockito. Is there another method to achieve the same other than reverting back to JUnit 4?
From Mockito 3.4.0 (2020-07-10), it is possible to mock static methods out of the box even in JUnit 5, without any extension.
In the documentation, you can find an example: 48. Mocking static methods (since 3.4.0)
Important note: You need to use inline mock maker. So the dependency to use is not the core one:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.4.6</version>
<scope>test</scope>
</dependency>
Example:
Class under test:
package teststatics;
public class FooWithStatics {
public static Long noParameters() {
return System.currentTimeMillis();
}
public static String oneParameter(String param1) {
return param1.toUpperCase();
}
}
Test class:
package teststatics;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class FooWithStaticsTest {
#Test
void testStatic() {
// Before mock scope, usual behavior.
assertNotEquals(0L, FooWithStatics.noParameters());
assertNotEquals("yyy", FooWithStatics.oneParameter("xxx"));
// Mock scope
try (MockedStatic mocked = mockStatic(FooWithStatics.class)) {
// Mocking
mocked.when(FooWithStatics::noParameters).thenReturn(0L);
mocked.when(() -> FooWithStatics.oneParameter("xxx")).thenReturn("yyy");
// Mocked behavior
assertEquals(0L, FooWithStatics.noParameters());
assertEquals("yyy", FooWithStatics.oneParameter("xxx"));
// Verifying mocks.
mocked.verify(times(1), FooWithStatics::noParameters);
mocked.verify(times(1), () -> FooWithStatics.oneParameter("xxx"));
}
// After mock scope returns to usual behavior.
assertNotEquals(0L, FooWithStatics.noParameters());
assertNotEquals("yyy", FooWithStatics.oneParameter("xxx"));
}
}
The short answer is no, as the Mockito team is done with their work and is waiting for the JUnit team for an extension and are discussing here a lot.
With some overhead you can: As JUnit 5 provides support for running legacy JUnit 4, and there you can use Mockito. So you can create tests in Junit4 for these cases:
A sample project for migration setup with gradle and with mvn. From there I am using PowerMock 2.0 beta with Mockito 2.
The reason why Mockito doesn't provide static methods mocking at the moment is because of the common belief that static method shouldn't need to be mocked.
However, there is an open item for Mockito here that discusses the issue.
While this doesn't answer your question, in general it tells you why you shouldn't need the feature at all or will allow you to join the conversation with your ideas.
Make sure to have mockito-inline dependency in your POM file
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>3.6.28</version>
<scope>test</scope>
</dependency>
In my case I had to test scenario where exception thrown static method encode() of URLEncoder Class, so for that
try (MockedStatic theMock = mockStatic(URLEncoder.class)) {
theMock.when(() -> URLEncoder.encode("Test/11", StandardCharsets.UTF_8.toString()))
.thenThrow(UnsupportedEncodingException.class);
when(restClient.retrieveByName("Test%2F11")).thenReturn(null);
Assertions.assertThrows(ResponseStatusException.class, ()->service.retrieveByName("Test/11"));
}
We can mock a static method by JMockit.
JMockit is used for mocking the external dependencies outside the test boundary, similar to Mockito and other such mocking libraries.
The most important feature of JMockit is that it lets us mock anything, even the things that are hard to mock with other libraries such as constructors, static and final methods. It even allows mocking the member fields and initialization blocks as well.
Follow the below steps to enable JMockit:
The JMockit artifact is located in the central Maven repository, add the JMockit dependency in pom.xml
<!-- https://mvnrepository.com/artifact/org.jmockit/jmockit -->
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.49</version>
<scope>test</scope>
</dependency>
Mock the Class method in TestClass:
public class TestClass{
#Test
public void testMethod() {
new MockUp<ClassName>(){
#Mock
//mock the method here
};
}
}
Follow the tutorial to know more about how to use the JMockit.

PowerMockito mock indirect static method

Class to test
public class Randomer {
public int get() {
return (int) Math.random() + 1;
}
}
The test class
package org.samiron;
import static org.junit.Assert.assertEquals;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.api.support.membermodification.MemberMatcher;
import org.powermock.api.support.membermodification.MemberModifier;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.testng.annotations.Test;
/**
* #author samiron
*
*/
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Randomer.class, Math.class })
public class RandomerTest {
#Test
public void shouldAddUpDieRollsCorrectly() throws Exception {
PowerMockito.spy(Math.class);
MemberModifier.stub(MemberMatcher.method(Math.class, "random")).toReturn(2.0);
Randomer d = new Randomer();
assertEquals(3, d.get());
}
}
Always getting java.lang.AssertionError: expected:<3> but was:<1>
Whats going wrong here? To be honest, every time I came across a situation to mock a static function I try to find a way around instead of wasting time. So need your help to figure out the exact solution.
The sole purpose of the example classes is to demonstrate that Math.random() function is not mocked and therefore not returning the desired value.
General realization
Mocking is a every essential tool while writing tests. Although mocking on instances works quite as expected but mocking static methods seems to be a real complicated with so many combinations of mocking libraries and so many options to support just few simple scenarios. This should be streamlined.
Libraries used:
mockito-all-1.9.5.jar
powermock-mockito-release-full-1.5.1-full.jar
This test passes thereby proving that the static call Math.random() is successfully mocked:
#RunWith(PowerMockRunner.class)
// tell PowerMock about (a) the class you are going to test and (b) the class you are going to 'mock static'
#PrepareForTest({Randomer.class, Math.class })
public class RandomerTest {
#Test
public void shouldAddUpDieRollsCorrectly() throws Exception {
// prepare PowerMock for mocking statics on Math
PowerMockito.mockStatic(Math.class);
// establish an expectation for what Match.random() should return
PowerMockito.when(Math.random()).thenReturn(2.0);
Randomer d = new Randomer();
assertEquals(3, d.get());
}
}
The main differences between this and what you posted in your question are is that I am using ...
PowerMockito.mockStatic(Math.class) and PowerMockito.when(Math.random()).thenReturn(2.0)
... instead of:
PowerMockito.spy(Math.class) and MemberModifier.stub(MemberMatcher.method(Math.class, "random")).toReturn(2.0)
Also, in your OP the example code uses a mixture of JUnit (org.powermock.modules.junit4.PowerMockRunner) and TestNG (org.testng.annotations.Test) whereas in my example I am just using JUnit.
The above has been verified with
junit:4.12
powermock-module-junit4:1.7.0
powermock-api-mockito2:1.7.0
There seems to be a library mismatch here.
In the comments you stated to use the following dependencies (without the convenience of Maven):
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-mockito-release-full</artifactId>
<version>1.5.1</version>
<scope>test</scope>
</dependency>
I got your code working using these:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.7.0</version>
<scope>test</scope>
</dependency>

How to use Mockito with JUnit5

How can I use injection with Mockito and JUnit 5?
In JUnit4 I can just use the #RunWith(MockitoJUnitRunner.class) Annotation. In JUnit5 is no #RunWith Annotation?
There are different ways to use Mockito - I'll go through them one by one.
Manually
Creating mocks manually with Mockito::mock works regardless of the JUnit version (or test framework for that matter).
Annotation Based
Using the #Mock-annotation and the corresponding call to MockitoAnnotations::initMocks
to create mocks works regardless of the JUnit version (or test framework for that matter but Java 9 could interfere here, depending on whether the test code ends up in a module or not).
Mockito Extension
JUnit 5 has a powerful extension model and Mockito recently published one under the group / artifact ID org.mockito : mockito-junit-jupiter.
You can apply the extension by adding #ExtendWith(MockitoExtension.class) to the test class and annotating mocked fields with #Mock. From MockitoExtension's JavaDoc:
#ExtendWith(MockitoExtension.class)
public class ExampleTest {
#Mock
private List list;
#Test
public void shouldDoSomething() {
list.add(100);
}
}
The MockitoExtension documentation describes other ways to instantiate mocks, for example with constructor injection (if you rpefer final fields in test classes).
No Rules, No Runners
JUnit 4 rules and runners don't work in JUnit 5, so the MockitoRule and the Mockito runner can not be used.
Use Mockito's MockitoExtension. The extension is contained in a new artifact mockito-junit-jupiter:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>4.5.1</version>
<scope>test</scope>
</dependency>
It allows you to write tests as you would have with JUnit 4:
import org.mockito.junit.jupiter.MockitoExtension;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
#ExtendWith(MockitoExtension.class)
class MyTest {
#Mock
private Foo foo;
#InjectMocks
private Bar bar; // constructor injection
...
}
There are different ways to do but the cleaner way and that also respects the JUnit 5 philosophy is creating a org.junit.jupiter.api.extension.Extension for Mockito.
1) Creating mocks manually makes lose the benefit of additional Mockito checks to ensure you use correctly the framework.
2) Calling MockitoAnnotations.initMocks(this) in every test classes is boiler plate code that we could avoid.
And making this setup in an abstract class is not a good solution either.
It couples every test classes to a base class.
If then you need a new base test class for good reasons, you finish with a 3- level class hierarchy. Please avoid that.
3) Test Rules is a JUnit 4 specificity.
Don't even think of that.
And the documentation is clear about that :
However, if you intend to develop a new extension for JUnit 5 please
use the new extension model of JUnit Jupiter instead of the rule-based
model of JUnit 4.
4) Test Runner is really not the way to extend the JUnit 5 framework.
JUnit 5 simplified the hell of the Runners of JUnit 4 by providing an extension model for writing tests thanks to JUnit 5 Extensions.
Don't even think of that.
So favor the org.junit.jupiter.api.extension.Extension way.
EDIT : Actually, Mockito bundles a jupiter extension : mockito-junit-jupiter
Then, very simple to use :
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class FooTest {
...
}
Here is an addition to the excellent answer of Jonathan.
By adding as dependency the mockito-junit-jupiter artifact, the use of #ExtendWith(MockitoExtension.class) produced the following exception as the test is executed :
java.lang.NoSuchMethodError:
org.junit.platform.commons.support.AnnotationSupport.findAnnotation(Ljava/util/Optional;Ljava/lang/Class;)Ljava/util/Optional;
THe problem is that mockito-junit-jupiter depends on two independent libraries.
For example for mockito-junit-jupiter:2.19.0 :
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.19.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.1.0</version>
<scope>runtime</scope>
</dependency>
The problem was I used junit-jupiter-api:5.0.1.
So as junit-jupiter-api moves still often in terms of API, make sure you depend on the same version of junit-jupiter-api that mockito-junit-jupiter depends on.
You have to use the new #ExtendWith annotation.
Unfortunately there is no extension released yet.
On github you can see a beta implementation for the extension. as a example demo test.

Categories

Resources