Martin Meyer opened SPR-14083 and commented

If I have a Transaction Manager running in unit tests, it becomes impossible for Mockito to properly initialize and inject mocks. This problem seems to be related to usage of proxy classes, not specific to using a transaction manager.

Here's a basic example of a test class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {EnableTransactionManager.class})
public class SomeTest {
    @Mock
    private MockBean someMockedBean;

    @Autowired
    @InjectMocks
    private RealBean someRealBean;

    @Before
    public void before() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void someTest() {
        someRealBean.doSomething();
    }
}

What I see in the debugger is that on the someRealBean.doSomething() line, someRealBean contains a mock reference to my MockBean instance. But when I set the next breakpoint within RealBean#doSomething() and go there, the local field within the object is not a mock one, it's a real @Autowire'd bean.

In real terms, the impact on me is that tests fail whenever I try to implement a TransactionManager and have it configured during tests. I end up trying to call DAO methods that should have been mocked, and it tries to issue database calls on a DataSource that isn't really ready to service them.

I think the problem is a race between Mockito and Spring to performing their setup. I think the solution is for SpringJUnit4ClassRunner to automagically call the equivalent of MockitoAnnotations.initMocks(this) if org.mockito.Mockito is in the classpath. This would mean adding an optional dependency on Mockito.

There was an issue opened against Mockito for this, which was closed as WONTFIX. They suggested that it be fixed in Spring instead of bringing in a Spring dependency there.

The obvious workaround here is to not enable anything that will do AOP proxying during your unit tests.


Affects: 4.2.5

Comment From: spring-projects-issues

Michael Huffman commented

I do not believe this is a Spring issue. Mockito's PropertyAndSetterInjection appears to have a design flaw/limitation in that it uses field declaration of a class as a means to determine candidate injection types and properties. When a proxy is used, particularly a CGLIB proxy, the fields have no correlation to the public properties (setters) available for use in injection. Therefore, the injection filtering does not find the properties/setters suitable to inject. I have reported a comment on your original bug report in Mockito.

Comment From: spring-projects-issues

Martin Meyer commented

Has anyone looked back at this problem in the past two years? This problem persists and from a user perspective it really seems like I shouldn't have to go so far out of my way to inject mocks. Mockito has been under much heavier development lately, and with all that's changed in JUnit 5 and Spring Framework 5 maybe fixing this problem might be easier than it was when I originally filed the issue..?

Comment From: spring-projects-issues

Bulk closing outdated, unresolved issues. Please, reopen if still relevant.

Comment From: elreydetodo

This is definitely still relevant. It has tripped up other developers on almost every project I've worked on involving Spring Framework. We always get into a state early on where unit tests aren't passing and we can't figure out why, and it always turns out it's because injecting mocks on something being proxied by cglib doesn't work as expected. Once we re-realize the problem, we start using ReflectionTestUtils to set the field, but then everyone forgets why we're doing it in some tests and not others, and we run amok using ReflectionTestUtils almost everywhere instead of @InjectMocks.

We really could use a better system to do this for us. This shouldn't be so hard and manual. Even discovering that this is a problem isn't that easy.

Comment From: philwebb

@elreydetodo Have you seen the @MockBean annotation from Spring Boot?

Comment From: philwebb

A similar proxy issue was raised in Spring Boot https://github.com/spring-projects/spring-boot/issues/6871 with @MockBean

Comment From: spring-projects-issues

If you would like us to look at this issue, please provide the requested information. If the information is not provided within the next 7 days this issue will be closed.

Comment From: elreydetodo

Actually it looks like that would resolve the problem. I'm reading a little into that issue and the Javadoc of MockBean and MockitoTestExecutionListener, and it sounds like this thing is what I want, except for some reason it's done in Spring Boot instead of as part of the core framework.

Can we get that annotation and the test execution listener promoted to spring-test?

Comment From: elreydetodo

Please note that I'm not using Spring Boot, so I can't simply use that annotation and be done with it. Spring Boot doesn't seem like the appropriate place for such fundamental functionality to live.

Comment From: greylurk

It's been about a year since it was suggested to move the MockBean annotation (and its affiliated functionality) from the spring-boot project to the spring-test project. What's involved in completing that move?

Comment From: sbrannen

Superseded by #29917 which introduces support for @TestBean, @MockitoBean, and @MockitoSpyBean in the spring-test module in Spring Framework 6.2.