spring-projects / spring-boot

Spring Boot
https://spring.io/projects/spring-boot
Apache License 2.0
74.53k stars 40.53k forks source link

ApplicationEventPublisher mocked with MockBean is not injected in component under test #6060

Closed jnizet closed 8 years ago

jnizet commented 8 years ago

Version of Spring Boot: 1.4.0.M3

I have an integration test, annotated with

@RunWith(SpringRunner::class)
@SpringBootTest
@AutoConfigureMockMvc

and where I have

    @MockBean
    private lateinit var mockApplicationEventPublisher: ApplicationEventPublisher

    @Autowired
    private lateinit var mockMvc: MockMvc

Kotlin code, equivalent to

    @MockBean
    private ApplicationEventPublisher mockApplicationEventPublisher;

    @Autowired
    private MockMvc mockMvc;

The controller under test does call publishEvent() on the event publisher, but when I verify that the calls have been made using

    verify(mockApplicationEventPublisher).publishEvent(ScoreSubmittedEvent()))

I get the following error:

Wanted but not invoked:
org.springframework.context.ApplicationEventPublisher#0 bean.publishEvent(...);
-> at com.ninja_squad.ng2propack.pack.ScoreControllerTest.should submit score(ScoreControllerTest.kt:89)
Actually, there were zero interactions with this mock.

When I debug, I notice that the publisher in the test is indeed a Mockito-generated proxy, but that the publisher injected in the controller under test is the non-mocked instance of org.springframework.web.context.support.GenericWebApplicationContext.

I guess the root caus is that the bean implementing ApplicationEventPublisher is not an actual separate bean, but is the spring application context itself.

A workaround would be to use my own event publisher bean that would simply delegate to the actual spring event publisher, but that's something I shouldn't have to do.

philwebb commented 8 years ago

As far as I can tell this is a limitation of the Spring Framework. It appears that an ApplicationEventPublisher cannot be registered as a regular bean. Take a look at AbstractApplicationContext.prepareBeanFactory and specifically the Javadoc for beanFactory.registerResolvableDependency.

You could add a additional bean to your test application context and use @EventListener to verify the event was sent. Alternatively you might be able to spy on the ApplicationEventMulticaster.

jnizet commented 8 years ago

OK. I'll open a bug for Spring Framework then, because I think that workarounds shouldn't be necessary to support that common use-case.

kgemmert-r7 commented 7 years ago

Is there any update on that bug report, I am hitting a similar problem.

snicoll commented 7 years ago

@kgemmert-r7 this issue is closed, check the issue that @jnizet has created.

renannprado commented 6 years ago

I've just had the same issue. My workaround was the following

@Bean
@Primary
GenericApplicationContext genericApplicationContext(final GenericApplicationContext gac) {
    return Mockito.spy(gac);
}
idrisssakhi commented 4 years ago

Version of Spring Boot: 1.4.0.M3

I have an integration test, annotated with

@RunWith(SpringRunner::class)
@SpringBootTest
@AutoConfigureMockMvc

and where I have

    @MockBean
    private lateinit var mockApplicationEventPublisher: ApplicationEventPublisher

    @Autowired
    private lateinit var mockMvc: MockMvc

Kotlin code, equivalent to

    @MockBean
    private ApplicationEventPublisher mockApplicationEventPublisher;

    @Autowired
    private MockMvc mockMvc;

The controller under test does call publishEvent() on the event publisher, but when I verify that the calls have been made using

    verify(mockApplicationEventPublisher).publishEvent(ScoreSubmittedEvent()))

I get the following error:

Wanted but not invoked:
org.springframework.context.ApplicationEventPublisher#0 bean.publishEvent(...);
-> at com.ninja_squad.ng2propack.pack.ScoreControllerTest.should submit score(ScoreControllerTest.kt:89)
Actually, there were zero interactions with this mock.

When I debug, I notice that the publisher in the test is indeed a Mockito-generated proxy, but that the publisher injected in the controller under test is the non-mocked instance of org.springframework.web.context.support.GenericWebApplicationContext.

I guess the root caus is that the bean implementing ApplicationEventPublisher is not an actual separate bean, but is the spring application context itself.

A workaround would be to use my own event publisher bean that would simply delegate to the actual spring event publisher, but that's something I shouldn't have to do.

@TestConfiguration static class contextConfiguration {

@Bean
@Primary
public ApplicationEventPublisher applicationEventPublisher(ApplicationEventPublisher app) {
  return Mockito.spy(app);
}

}