I'm struggling with providing instances of Beans f...
# spring
d
I'm struggling with providing instances of Beans for SpringBoot tests, can anyone see what I'm doing wrong please:
Copy code
@SpringBootApplication
class DemoApplication

fun main(args: Array<String>) {
    runApplication<DemoApplication>(*args)
}

@Component
class NormalComponent() {
    fun value() = "Hi"
}
Test:
Copy code
@TestConfiguration
class TestConfig {

	@Bean
	fun normalComponentMock(): NormalComponent {
		val mock =  mock(NormalComponent::class.java)
		`when`(mock.value()).thenReturn("I AM A MOCK!")
		return mock
	}
}

@SpringBootTest
@Import(TestConfig::class)
class DemoApplicationTests(
	@Autowired val norm: NormalComponent
) {
	@Test
	fun normInjectedIntoTest() {
		assertEquals("I AM A MOCK!", norm.value())
	}
}
The error:
Copy code
org.junit.jupiter.api.extension.ParameterResolutionException: Failed to resolve parameter [com.example.demo.NormalComponent norm] in constructor [public com.example.demo.DemoApplicationTests(com.example.demo.NormalComponent)]: No qualifying bean of type 'com.example.demo.NormalComponent' available: expected single matching bean but found 2: normalComponent,normalComponentMock
I know I could use
@MockBean
- this is just a simple example where the bean I want to inject to the instance. Bit confused why the mock conflict is happening when I have using the
@TestConfiguration
and the
@Import
annotation - thanks in advance 🙏
k
does not @SpringBootApplication componentScan, so that in your test you have your normal and mock beans?
t
yes, you also get the same message from the exception
No qualifying bean of type 'com.example.demo.NormalComponent' available: expected single matching bean but found 2: normalComponent,normalComponentMock
`TestConfiguration`s are not picked by component scan (I think)
one workaround could be to use `@Primary`on `normalComponentMock`(again, I think, I would just have gone with mockbean), or if you need the same mockbean in multiple test you can use `@ConditionalOnXYZ`and disable
class NormalComponent
.eg
Copy code
@Component
@ConditionalOnProperty(...)
class NormalComponent()
d
does not @SpringBootApplication componentScan, so that in your test you have your normal and mock beans?
Yeah I think that is happening. If I put
@Primary
in the non-test Bean we don't get the test Mockbean injected into the test
yeah, with
@Primary
... expected: <I AM A MOCK!> but was: <Hi>
So I guess that's the crux of my problem, I want the test bean injecting into the test
j
You should put @Primary to your mock bean, and also set
Copy code
spring:
  main:
    allow-bean-definition-overriding: true
this way your mock overrides the actual bean
t
yep,
@Primary
goes on your test bean
d
gotcha!
tests pass!
so in this article (which may be a bit old) how did they get it work without using primary: https://mkyong.com/spring-boot/spring-boot-how-to-init-a-bean-for-testing/
j
probably because they introduce RestTemplateBuilder as a bean which "overrides" the one that is used under the hood, and what is injected to tests is actually TestRestTemplate (which was created by RestTemplateBuilder)
d
I see
Cool, think this give me enough to be getting on with. ^ Thank you all for your help! :)
t
they leveraged
@Conditional
. there is a
RestTemplateAutoConfiguration
that creates a default
RestTemplateBuilder
, but that default one is annotated with
@ConditionalOnMissingBean
. that means that if your application defines another
RestTemplateBuilder
the one in your application is preferred
d
oh I see, thanks I've not used the conditional anotations
t
if I am not wrong, you cannot do the same in your case, as both beans are defined by your application. maybe you can if both have the same name
684 Views