Home > Mobile >  How to test event listener in spring-boot
How to test event listener in spring-boot

Time:08-31

I am trying to test an event listener like this one

@Component
class EventHandler {
     @EventListener
    fun handler(event: SomeEvent) {      //SomeEvent is has properties source and someObject
        ...
    }
}

My test is in this format

@SpringBootTest
class SendWelcomeEmailTest {
    @Autowired
    lateinit var publisher: ApplicationEventPublisher
    
    @Test
    fun test(){
        val someObject = SomeObject()
        val event = SomeEvent(this, someObject)
        val listener = mockkClass(EventHandler::class)

        publisher.publishEvent(event)
        verify { listener.handler(any()) }
    }
}

When I run this test I am getting an

java.lang.AssertionError: Verification failed: call 1 of 1: EventHandler(#4).handler(matcher<SomeEvent>())) was not called

error, however by using debugger I can see that handler function is entered with correct parameters. So why doesn't verify catch the execution of handler function?

CodePudding user response:

Just because you call mockkClass(EventHandler::class), the bean registered in the Spring container does not magically get replaced by your local mock. The original bean is still called (as you verified using the debugger), and your local mock does not participate in the Spring container.

In general, you can use @MockBean from Spring (check the Javadocs for details), which will create a Mockito mock and register it in the Spring container, replacing the original bean (this is done behind the scenes by the Spring Test Framework).

But I think in your case that might not work, because the bean will probably not be registered as an event handler, but you could try.

If @MockBean does not work, the only option I see is to explicitly declare a hand-made "mock" EventHandler subclass and mark it with @Primary, so that it gets chosen over the original one by Spring. Then you can add any verification method you like there (e.g. in the mock handler method, save events that arrive into a local state, and add a method to read the recorded state).

CodePudding user response:

So with springmockk:

Gradle:

testImplementation("com.ninja-squad:springmockk:3.1.1")

Maven:

<dependency>
 <groupId>com.ninja-squad</groupId>
 <artifactId>springmockk</artifactId>
 <version>3.1.1</version>
 <scope>test</scope>
</dependency>

This test:

@SpringBootTest
class SendWelcomeEmailTest {
  @Autowired
  lateinit var publisher: ApplicationEventPublisher
  @MockkBean(relaxed = true)// strict by default!
  private lateinit var listenerMock: EventHandler
  @Test
  fun test() {
    
    val someObject = SomeObject()
    val event = SomeEvent(this, someObject)
    
    publisher.publishEvent(event)

    verify { listenerMock.handler(any()) }
  }
}

..should pass!

Please also consider:

If you want to make sure Mockito (and the standard MockBean and SpyBean annotations) is not used, you can also exclude the mockito dependency:

testImplementation("org.springframework.boot:spring-boot-starter-test") { exclude(module = "mockito-core")}

...limitations, compatibility.

  • Related