I Have the following abstract unit test class that all my concrete unit test classes extend:
@ExtendWith(SpringExtension.class)
//@ExtendWith(MockitoExtension.class)
@SpringBootTest(
classes = PokerApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
public abstract class AbstractUnitTests {
@MockBean
public RoundService roundService;
@MockBean
public RoundRepository roundRepository;
}
What is the difference between using @ExtendWith(SpringExtension.class)
or @ExtendWith(MockitoExtension.class)
?
I ask as using either of the annotations seems to make no difference and both work respectively in my code - allowing me to use Junit5. So why do both work?
Concrete test class:
@DisplayName("Test RoundService")
public class RoundsServiceTest extends AbstractUnitTests {
private static String STUB_USER_ID = "user3";
// class under test
@InjectMocks
RoundService roundService;
private Round round;
private ObjectId objectId;
@BeforeEach //note this replaces the junit 4 @Before
public void setUp() {
initMocks(this);
round = Mocks.round();
objectId = Mocks.objectId();
}
@DisplayName("Test RoundService.getAllRoundsByUserId()")
@Test
public void shouldGetRoundsByUserId() {
// setup
given(roundRepository.findByUserId(anyString())).willReturn(Collections.singletonList(round));
// call method under test
List<Round> rounds = roundService.getRoundsByUserId(STUB_USER_ID);
// asserts
assertNotNull(rounds);
assertEquals(1, rounds.size());
assertEquals("user3", rounds.get(0).userId());
}
}
Relevant Build.gradle section :
dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb'
compile group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: '2.2.2.RELEASE'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
implementation 'junit:junit:4.12'
}
test {
useJUnitPlatform()
}
Mockito provides an implementation for JUnit5 extensions in the library – mockito-junit-jupiter.
@RunWith is an old annotation from JUnit 4 to use test runners. If you're using JUnit 5 (Jupiter), you should use @ExtendWith to use JUnit extensions.
Class SpringExtension. SpringExtension integrates the Spring TestContext Framework into JUnit 5's Jupiter programming model. To use this extension, simply annotate a JUnit Jupiter based test class with @ExtendWith(SpringExtension.
The "@ExtendWith" annotation that was seen in the sample story earlier is a markup interface provided by Jupiter. It is a declarative way to register a custom extension on a test class or method. It tells Jupiter's test engine to invoke the custom extension for the given class or method.
What is a Junit Extension
The purpose of Junit 5 extensions is to extend the behavior of test classes or methods
source
Read on Junit 5 Extension Model & @ExtendWith
annotation :here
SpringExtension
SpringExtension integrates the Spring TestContext Framework into JUnit 5's Jupiter programming model.
public class SpringExtension
extends Object
implements BeforeAllCallback, AfterAllCallback, TestInstancePostProcessor, BeforeEachCallback, AfterEachCallback, BeforeTestExecutionCallback, AfterTestExecutionCallback, ParameterResolver{..}
MockitoExtension
This extension is the JUnit Jupiter equivalent of our JUnit4 MockitoJUnitRunner
public class MockitoExtension
extends java.lang.Object
implements BeforeEachCallback, AfterEachCallback, ParameterResolver{..}
As it can be seen , SpringExtension
implements a lot more extensions than MockitoExtension
.
Also @SpringBootTest
is meta annotated with @ExtendWith(SpringExtension.class)
and which means every time your tests are extended with SpringExtension
.
@MockBean
is a Spring test framework annotation and used along with @ExtendWith(SpringExtension.class)
To observe the difference try the following
ExtendWith
only MockitoExtension
@ExtendWith(MockitoExtension.class)
class TestServiceTest {
@MockBean
TestService service;
@Test
void test() {
assertNotNull(service); // Test will fail
}
}
ExtendWith
only SpringExtension
@ExtendWith(SpringExtension.class)
class TestServiceTest {
@MockBean
TestService service;
@Test
void test() {
assertNotNull(service); // Test succeeds
}
}
ExtendWith
with both SpringExtension
and MockitoExtension
@ExtendWith(MockitoExtension.class)
@ExtendWith(SpringExtension.class)
class TestServiceTest {
@MockBean
TestService service;
@Test
void test() {
assertNotNull(service); // Test succeeds
}
}
Both works in your case because of the @SpringBootTest
annotation for the test class as explained.
To answer the question : When to use @ExtendWith
Spring or Mockito? ,
When the test requires a Spring Test Context ( to autowire a bean / use of @MockBean
) along with JUnit 5's Jupiter programming model use @ExtendWith(SpringExtension.class)
. This will support Mockito annotations as well through TestExecutionListeners.
When the test uses Mockito and needs JUnit 5's Jupiter programming model support use @ExtendWith(MockitoExtension.class)
Hope this helps
When to use @ExtendWith(SpringExtension.class) or @SpringBootTest?
When you use Integration test -@SpringBootTest annotation- or any slice test -@xxxTest annotations- you don't need @ExtendWith(SpringExtension.class) annotation since mentioned annotations include it.
If you test @ConfigurationProperties, @Service, @Component annotated class ( not defined in slice test cases - ref:Spring Boot Reference Document Testing/Auto-configured / SLICED Tests item-, you may use @ExtendWith(SpringExtension.class) instead of @SpringBootTest.
Observation: I expect that a test with @ExtendWith(SpringExtension.class) faster than the same test with @SpringBootTest.When I perform a test in Eclipse i observed the reverse.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With