How can I use injection with Mockito and JUnit 5?
In JUnit 4, I can just use the @RunWith(MockitoJUnitRunner.class)
annotation.
In JUnit 5, there is no @RunWith
Annotation.
How can I use injection with Mockito and JUnit 5?
In JUnit 4, I can just use the @RunWith(MockitoJUnitRunner.class)
annotation.
In JUnit 5, there is no @RunWith
Annotation.
There are different ways to use Mockito - I'll go through them one by one.
Creating mocks manually with Mockito::mock
works regardless of the JUnit version (or test framework for that matter).
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).
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).
JUnit 4 rules and runners don't work in JUnit 5, so the MockitoRule
and the Mockito runner can not be used.
@Test
needs to be public or "package private" is good enough? –
Upton 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
...
}
@ExtendWith(MockitoExtension.class)
is equivalent of @RunWith(MockitoJUnitRunner.class)
of the JUnit4 –
Irs 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.
mockito-junit-jupiter
pull the proper version of junit-jupiter-api
? –
Venus mockito-junit-jupiter:2.19.0
. While JUnit Jupiter versions start with 5
. mockito-junit-jupiter should have specified in its artifact identifier the two things (Mockito version and JUnit Jupiter version) to make things clearer. For example mockito-junit-jupiter-5.1:2.19.0
to convey that the library is designed for JUnit Jupiter 5.1. –
Entoil MockitoExtension
doesn't appear to exist in mockito-core
version 3.0.0. –
Deliladelilah mockito-junit-jupiter
–
Entoil For Kotlin and when annotating a field, make sure to mark it with lateinit
:
@ExtendWith(MockitoExtension.class)
class MyTest {
@Mock
lateinit var list: List
@Test
fun `Test should work`() {
list.add(100)
}
}
© 2022 - 2024 — McMap. All rights reserved.