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'
Related
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.
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.
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.
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.
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