If you are new to Java testing, you might wonder: are JUnit 6 and Mockito competitors? Do I need both? When does Mockito replace JUnit 6? The answer is that they are not alternatives β they are complementary tools with completely different jobs. JUnit 6 runs your tests. Mockito creates the fake objects your tests use. This guide clarifies their distinct roles, shows how they integrate, and answers every question developers have about which tool does what.
What JUnit 6 Does
JUnit 6 is a test runner and test framework. Its responsibilities are:
- Discovering test classes and test methods on the classpath
- Managing the test lifecycle (
@BeforeEach,@AfterEach, etc.) - Executing test methods and reporting pass/fail/skip
- Providing assertion methods (
assertEquals,assertThrows,assertAll) - Supporting parameterized tests, dynamic tests, and parallel execution
- Integrating with build tools (Maven, Gradle) and IDEs
What Mockito Does
Mockito is a mocking framework. Its responsibilities are:
- Creating fake (mock) implementations of interfaces and classes
- Defining return values for method calls on mocks (stubbing)
- Verifying that methods were called with expected arguments
- Creating spies that wrap real objects with selective overrides
- Capturing method arguments for detailed inspection
Mockito does NOT run tests. JUnit 6 does NOT create mocks. They work together.
The Division of Labour
// This test uses BOTH JUnit 6 and Mockito β each doing its own job
import org.junit.jupiter.api.*; // JUnit 6: @Test, @BeforeEach, @DisplayName
import org.mockito.*; // Mockito: @Mock, @InjectMocks
import org.mockito.junit.jupiter.*; // Bridge: connects the two
import static org.junit.jupiter.api.Assertions.*; // JUnit 6: assertions
import static org.mockito.Mockito.*; // Mockito: when(), verify()
// JUnit 6 discovers and runs this class
@ExtendWith(MockitoExtension.class) // Mockito registers as a JUnit 6 extension
class OrderServiceTest {
// ---- Mockito's job: create fake dependencies ----
@Mock
private OrderRepository orderRepository; // fake repository
@Mock
private EmailService emailService; // fake email service
@InjectMocks
private OrderService orderService; // real class under test
// ---- JUnit 6's job: lifecycle management ----
@BeforeEach
void printTestStart() {
System.out.println("Test starting...");
}
// ---- JUnit 6's job: discover and execute this test ----
@Test
@DisplayName("Placing an order persists it and sends a confirmation email")
void placingOrderPersistsItAndSendsEmail() {
// ---- Mockito's job: define stub behaviour ----
Order savedOrder = new Order(1L, "[email protected]", 99.99, OrderStatus.CONFIRMED);
when(orderRepository.save(any(Order.class))).thenReturn(savedOrder);
// ---- The actual call under test ----
Order result = orderService.placeOrder("[email protected]", 99.99);
// ---- JUnit 6's job: assert the result ----
assertNotNull(result.getId(), "Order ID must be assigned");
assertEquals(OrderStatus.CONFIRMED, result.getStatus());
// ---- Mockito's job: verify interactions ----
verify(orderRepository, times(1)).save(any(Order.class));
verify(emailService, times(1)).sendConfirmation("[email protected]");
}
}
How They Integrate: MockitoExtension
The bridge between JUnit 6 and Mockito is MockitoExtension β a JUnit 6 extension that processes Mockitoβs annotations at test lifecycle points:
// What MockitoExtension does internally:
// 1. Before each test method (@BeforeEach equivalent):
// - Scans the test class for @Mock, @Spy, @Captor, @InjectMocks fields
// - Creates mock objects for @Mock fields
// - Creates spy wrappers for @Spy fields
// - Injects mocks into the @InjectMocks field
// 2. After each test method (@AfterEach equivalent):
// - Detects unnecessary stubs (stubbing that was declared but never called)
// - Reports them as UnnecessaryStubbingException (strict mode default)
// - Resets mock state (optional, depends on strictness setting)
// STRICT mode (default): fails if you stub a method that is never called
@ExtendWith(MockitoExtension.class) // strict mode by default
// LENIENT mode: won't fail on unnecessary stubs
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
Without the Extension: Manual Mockito Setup
// Option 2: manual Mockito setup without MockitoExtension
// Useful when you have a reason not to use the extension
class OrderServiceManualMockTest {
private OrderRepository orderRepository;
private EmailService emailService;
private OrderService orderService;
@BeforeEach
void setUp() {
// Manually open mocks before each test
orderRepository = mock(OrderRepository.class);
emailService = mock(EmailService.class);
orderService = new OrderService(orderRepository, emailService);
}
@Test
void placingOrderCallsRepository() {
when(orderRepository.save(any())).thenReturn(new Order(1L, "[email protected]", 10.0, null));
orderService.placeOrder("[email protected]", 10.0);
verify(orderRepository).save(any());
}
}
Complete Responsibilities Reference
| Responsibility | JUnit 6 | Mockito |
|---|---|---|
| Discover test classes | β | β |
| Run test methods | β | β |
| Lifecycle hooks (@BeforeEach) | β | β |
| assertEquals, assertThrows | β | β |
| Parameterized tests | β | β |
| Create mock objects | β | β |
| Stub method return values | β | β |
| Verify method calls | β | β |
| Argument capture | β | β |
| Spy on real objects | β | β |
| Report to Maven/Gradle | β | β |
Frequently Asked Questions (FAQs)
Q1: Can I use Mockito without JUnit 6?
Yes. Mockito is a standalone library that works independently. You can call Mockito.mock(SomeClass.class) in any Java code without a test framework. However, in practice, Mockito is almost always used inside a test framework because you need something to run the tests. MockitoExtension is specific to JUnit 6; MockitoJUnitRunner was specific to JUnit 4; MockitoAnnotations.openMocks(this) works framework-independently.
Q2: Can I use JUnit 6 without Mockito?
Yes. JUnit 6 works perfectly without Mockito. For simple domain classes with no external dependencies, you do not need mocking at all β just instantiate the class directly. Mockito becomes necessary when the class under test depends on interfaces or classes that involve I/O, databases, HTTP, or other external systems that should not be called during unit tests.
Q3: When should I use Mockito verify() vs JUnit 6 assertions?
Use JUnit 6 assertions to verify the return value and state of the object under test. Use Mockito verify() to verify that the object under test called a dependency correctly. For example: assertEquals(CONFIRMED, order.getStatus()) (JUnit 6) verifies what the service returned; verify(emailService).send(...) (Mockito) verifies that the service called a collaborator. Both are necessary for complete test coverage of a service layer.
Q4: What happens if I forget @ExtendWith(MockitoExtension.class)?
Your @Mock fields will remain null because no one initialised them. The first test that accesses a mock field will throw a NullPointerException. The fix: either add @ExtendWith(MockitoExtension.class) to the class, or call MockitoAnnotations.openMocks(this) in a @BeforeEach method. IntelliJ IDEAβs inspections will warn you about this automatically.
Q5: Is there an alternative to Mockito for mocking in JUnit 6?
Yes. EasyMock and PowerMock are older alternatives. MockK is the preferred mocking library for Kotlin projects (with JUnit 6 integration). JMockit supports advanced scenarios including static method mocking. However, Mockito is by far the most widely used, best documented, and most actively maintained Java mocking library. For new Java projects using JUnit 6, Mockito is the default choice.
See Also
- JUnit 6 with Mockito: Mocking, Spying, and Best Practices
- JUnit 6 Extensions Model: Build Custom Extensions Step-by-Step
- Unit vs Integration vs E2E Testing in JUnit 6 (Practical Guide)
- JUnit 6 vs TestNG: Feature Comparison and When to Use What
- JUnit 6 Nullability: @Nullable, @NonNull and @NullMarked Explained
- JUnit 6 Tutorial: Complete Series Index
Conclusion
JUnit 6 and Mockito are not competitors β they solve different problems and work together seamlessly. JUnit 6 provides the test infrastructure: discovery, lifecycle, assertions, and reporting. Mockito provides test isolation: fake objects, stubbed behaviour, and interaction verification. Understanding their distinct roles makes you a better Java tester because you know exactly which tool to reach for when you need to assert on a result vs verify an interaction vs run a test in a specific order.
Next: JUnit 6 vs Spock vs TestNG: Best Testing Framework for Java? β the ultimate three-way comparison to help you choose the right framework for your team.