Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support strict stubbing for @MockitoBean and @MockitoSpyBean #34419

Open
sbrannen opened this issue Feb 13, 2025 · 0 comments
Open

Support strict stubbing for @MockitoBean and @MockitoSpyBean #34419

sbrannen opened this issue Feb 13, 2025 · 0 comments
Assignees
Labels
in: test Issues in the test module status: blocked An issue that's blocked on an external project change type: enhancement A general enhancement
Milestone

Comments

@sbrannen
Copy link
Member

sbrannen commented Feb 13, 2025

Overview

As explained in #33692, due to limitations in Mockito it is not currently possible to support strict stubbing for mocks and spies created via @MockitoBean and @MockitoSpyBean.

This issue therefore serves as a placeholder to collaborate with the Mockito team to investigate a way to provide such support.

Known Issues

  • It is impossible for two frameworks to use the MockitoSession simultaneously. Only one MockitoSession can exist at any given time for the current thread.
  • The MockitoTestExecutionListener that existed during the 6.2 milestone phase attempted to emulate the behavior of Mockito's own MockitoExtension; however, a Spring TestExecutionListener cannot achieve the same level of integration into JUnit Jupiter's extension model.
    • A Spring TestExecutionListener cannot access the enclosing test instances for @Nested test classes.
    • A Spring TestExecutionListener cannot store state in a parent TestContext (simply because there is no parent).
    • Consequently, the MockitoTestExecutionListener provided less benefit than using the MockitoExtension when it comes to JUnit Jupiter support.

Results of exploratory research

MockitoSession is effectively a "Unit of Work" that is bound to the current thread via a ThreadLocal. Mocks created between the "start" and "finish" of the session are tracked for the current thread only. Mocks created outside that session -- for example, before the session or in another thread -- are not tracked by the session. In addition, there is no API that allows one to attach an existing mock to a MockitoSession, and there is no API to query whether a MockitoSession already exists for the current thread.

The following provides an overview of how MockitoSession technically works.

  • startMocking() invokes: MockitoAnnotations.openMocks()
  • startMocking() registers: org.mockito.internal.junit.UniversalTestListener
  • finishMocking() unregisters: org.mockito.internal.junit.UniversalTestListener
  • finishMocking() invokes: Mockito.validateMockitoUsage()

Hypothetically (and this is not tested in any way, shape, or form), if we wanted to mimic MockitoSession support for mocks created via @MockitoBean and @MockitoSpyBean, we could track the MockCreationSettings in MockitoBeanOverrideMetadata.createMock() and then register a (subclass of) UniversalTestListener via Mockito.framework().addListener() and invoke CustomSubclassOfUniversalTestListener.onMockCreated(Object, MockCreationSettings) with the mock created by MockitoBeanOverrideMetadata and the saved MockCreationSettings in MockitoTestExecutionListener.beforeTestMethod(), and we could then invoke Mockito.framework().removeListener() and Mockito.validateMockitoUsage() in MockitoTestExecutionListener.afterTestMethod().

However, UniversalTestListener is an internal implementation detail of Mockito and resides in the org.mockito.internal.junit package, and we should ideally not rely on Mockito internals that may change unexpectedly.

Instead, we should approach the Mockito team to discuss alternatives to the current MockitoSession API and semantics that would allow us to provide similar support to mocks that we choose and within a scope that we define.

Related Issues

@sbrannen sbrannen added in: test Issues in the test module status: blocked An issue that's blocked on an external project change type: enhancement A general enhancement labels Feb 13, 2025
@sbrannen sbrannen added this to the 7.0.x milestone Feb 13, 2025
@sbrannen sbrannen self-assigned this Feb 13, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: test Issues in the test module status: blocked An issue that's blocked on an external project change type: enhancement A general enhancement
Projects
None yet
Development

No branches or pull requests

1 participant