Ninja-Squad/springmockk

@SpykBean results in NullPointerException when multiple bean candidates and none are primary

nmenger opened this issue · 2 comments

When there are multiple beans defined that are the same type and none are marked @Primary, @SpykBean resolution fails with the following error:

Caused by: java.lang.IllegalStateException: Unable to register spy bean <beanclass>
	at com.ninjasquad.springmockk.MockkPostProcessor.registerSpies(MockkPostProcessor.kt:286)
	at com.ninjasquad.springmockk.MockkPostProcessor.registerSpy(MockkPostProcessor.kt:224)
	at com.ninjasquad.springmockk.MockkPostProcessor.register(MockkPostProcessor.kt:148)
	at com.ninjasquad.springmockk.MockkPostProcessor.postProcessBeanFactory(MockkPostProcessor.kt:110)
	at com.ninjasquad.springmockk.MockkPostProcessor.postProcessBeanFactory(MockkPostProcessor.kt:95)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:325)
	at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:191)
	at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:746)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:564)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:731)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:408)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:307)
	at org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:136)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:141)
	at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:90)
	... 75 more
Caused by: java.lang.NullPointerException
	at com.ninjasquad.springmockk.MockkPostProcessor.registerSpies(MockkPostProcessor.kt:284)
	... 89 more

This should attempt to resolve the bean by the field name when there are multiple beans with the same type and none are marked @Primary.

Found on version 3.1.1 but it appears that it is not fixed in the latest version as of today: function with error.

I tried to reproduce the issue with the following test case:

@SpringBootTest(classes = [MyConfig::class])
class BugTest {

    @SpykBean
    lateinit var foo1: Foo

    @Test
    fun test() {
        assertThat(foo1).isNotNull()
    }
}

@Configuration
class MyConfig {
    @Bean
    fun foo1(): Foo = Foo1()

    @Bean
    fun foo2(): Foo = Foo2()
}

interface Foo {
    fun foo(): Unit
}

open class Foo1 : Foo {
    override fun foo() {
        println("foo1")
    }
}

open class Foo2 : Foo {
    override fun foo() {
        println("foo1")
    }
}

and indeed, I get a similar stack trace.

However, the same test also fails (later, and with a better explanation) when Mockito (i.e. SpyBean) is used with the same test. And the fix is the same in both cases: you need to define the name of the bean in the SpykBean (or SpyBean) annotation:

@SpykBean(name = "foo1")
lateinit var foo1: Foo

So, I'll leave this open to make sure that I get the same error as in a Mockito test, but resolving the bean by field name won't happen because I want to do the same thing as Spring Boot.

I released 4.0.2 which now throws the same exception as Spring Boot in that kind of situation.