JUnit 6 vs Mockito: Roles, Differences, and Integration

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

ResponsibilityJUnit 6Mockito
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

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.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.