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.