Mockito Unit Testing: Why You Should Not Mock the Class Under Test

Dec 07, 2025 · Programming · 6 views · 7.8

Keywords: Java | Unit Testing | Mockito | JUnit

Abstract: This article explores a common pitfall in Mockito unit testing where mocking the class under test leads to 'Wanted but not invoked' errors. Through a detailed example, it analyzes the cause of interaction缺失 and provides step-by-step solutions for correct test strategies, emphasizing the importance of testing real logic for code quality assurance.

Introduction

In Java development, unit testing is crucial for ensuring code reliability, with Mockito widely used to mock external dependencies for isolation. However, improper use of mocks can lead to test failures, such as the error message "Wanted but not invoked: Actually, there were zero interactions with this mock". Based on a real case, this article analyzes the cause of this error and provides solutions.

Problem Case

Consider a unit test scenario aimed at verifying whether a method calls the addTemplate method of PdfContentByte. The developer wrote the following test code:

public void computeEyeletsNoMirror() {
    PdfWriter pdfWriter = Mockito.mock(PdfWriter.class);
    PdfContentByte pdfContentByte = Mockito.mock(PdfContentByte.class);
    Mockito.when(pdfWriter.getDirectContent()).thenReturn(pdfContentByte);
    WithDefinitions withDefinitions = Mockito.mock(WithDefinitions.class); // Error: mocking the class under test
    float lineLeft = BORDER_LEFT + EYELET_MARGIN;
    float lineBottom = BORDER_BOTTOM + EYELET_MARGIN;
    withDefinitions.setEyeletDistMinH(20);
    withDefinitions.setEyeletDistMinV(20);
    withDefinitions.setMirror(false);
    withDefinitions.computeEyelets(pdfWriter);
    Mockito.verify(pdfContentByte).addTemplate(
        Mockito.any(PdfImportedPage.class),
        Mockito.eq(lineLeft),
        Mockito.eq(lineBottom)
    );
}

During test execution, an exception is thrown indicating that the addTemplate method was not called, because the WithDefinitions object is mocked, and its methods computeEyelets and setEyelet are replaced with default mock implementations that do nothing.

Cause Analysis

Mockito's mock mechanism creates a proxy object that intercepts all method calls and returns预设 values or empty behavior. When mocking the class under test (e.g., WithDefinitions), real methods do not execute, causing dependent method calls (e.g., addTemplate) never to occur. This violates the core principle of unit testing: verifying real code behavior.

Solution

According to best practices, use real objects to test their logic and only mock external dependencies. Correct the test code by replacing the mock with a real WithDefinitions instance:

public void computeEyeletsNoMirror() {
    PdfWriter pdfWriter = Mockito.mock(PdfWriter.class);
    PdfContentByte pdfContentByte = Mockito.mock(PdfContentByte.class);
    Mockito.when(pdfWriter.getDirectContent()).thenReturn(pdfContentByte);
    WithDefinitions withDefinitions = new WithDefinitions(); // Use real instance
    float lineLeft = BORDER_LEFT + EYELET_MARGIN;
    float lineBottom = BORDER_BOTTOM + EYELET_MARGIN;
    withDefinitions.setEyeletDistMinH(20);
    withDefinitions.setEyeletDistMinV(20);
    withDefinitions.setMirror(false);
    withDefinitions.computeEyelets(pdfWriter);
    Mockito.verify(pdfContentByte).addTemplate(
        Mockito.any(PdfImportedPage.class),
        Mockito.eq(lineLeft),
        Mockito.eq(lineBottom)
    );
}

This way, the computeEyelets method executes真的, calling setEyelet and triggering addTemplate, allowing the verification to pass. Meanwhile, PdfWriter and PdfContentByte are mocked to isolate PDF-related dependencies, ensuring the test focuses on business logic.

Additional Notes

In complex scenarios, if partial mocking is needed, Mockito's spy functionality can be used, but it is generally recommended to avoid mocking the class under test. Additionally, ensure mock objects are set up correctly to simulate dependency behaviors, such as using Mockito.when to define return values.

Conclusion

The goal of unit testing is to verify code logic correctness. Mockito should be applied to mock external dependencies, not the class under test itself. By adhering to this principle, you can avoid "Wanted but not invoked" errors, improve test coverage and reliability, and thus support high-quality software development.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.