#3745·mockito

Add support for final mocks

Author: topizioCreated Oct 15, 2025Updated May 26, 2026

Hey folks,

in Collibra some teams have been writing tests bypassing Mockito annotations so that test fields can be declared as final.

This unfortunately comes with a downside: it seems MockitoExtension does not take those mocks into account those mocks. In other words, those tests can do over stubbing and the extension does not protect them.

Please find hereby a test class I've just written to prove my point:

java
package com.collibra;

import com.collibra.test.mockito.MockitoLightweightExtension;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.util.UUID;

import static java.util.UUID.randomUUID;
import static org.assertj.core.api.BDDAssertions.then;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.mock;

class LightweightTest {

    interface Service {
        Object getById(UUID id);
    }

    @RequiredArgsConstructor
    static class Api {
        private final Service service;

        @SuppressWarnings("UnusedReturnValue")
        Object getById(UUID id) {
            return service.getById(id);
        }
    }

    abstract static class SimpleTest {
        private final Object expected = new Object();
        private final UUID id = randomUUID();

        abstract Service getService();

        abstract Api getApi();

        @Test
        void test() {
            // given
            var service = getService();
            given(service.getById(id)).willReturn(expected);
            // over-stubbing
            given(service.getById(randomUUID())).willReturn(new Object());

            // when
            var actual = getApi().getById(id);

            // then
            then(actual).isSameAs(expected);
        }
    }

    @Nested
    @ExtendWith(MockitoExtension.class)
    @Getter
    class WithAnnotationsOverStubbingIsDetected extends SimpleTest {

        @Mock
        private Service service;

        @InjectMocks
        private Api api;
    }

    @Nested
    @ExtendWith(MockitoExtension.class)
    @Getter
    class WithFinalFieldsTestPasses extends SimpleTest {

        private final Service service = mock();
        private final Api api = new Api(service);
    }

    /**
     * This also does not detect it and supposedly I'm following what's described in {@link MockitoExtension}'s
     * javadoc. Am I missing something? Or am I hitting a bug?
     */
    @Nested
    @ExtendWith(MockitoExtension.class)
    @Getter
    class WithConstructorInjectionTestPasses extends SimpleTest {
        private final Api api;
        private final Service service;

        WithConstructorInjectionTestPasses(@Mock Service service) {
            this.service = service;
            api = new Api(service);
        }
    }

    @Nested
    @ExtendWith(MockitoLightweightExtension.class)
    @Getter
    class WithFinalFieldsAndLightWeightedTestFails extends SimpleTest {

        private final Service service = mock();
        private final Api api = new Api(service);

    }
}

This issue relates with https://github.com/mockito/mockito/issues/3231 (which I've just closed as staled) but now the POV is different: it's not about performance, it is about detecting over stubbing.

The PR https://github.com/mockito/mockito/pull/3232 related to the closed issue This issue relates with https://github.com/mockito/mockito/issues/3231 still contain the definition of this custom extension. I've just rebased it and aligned it with the last version of our in house version