Annotation @ConditionalOnMissingBean not matching in test

Viewed 8647

I have two implementations of one interface - default and dev. I use @ConditionalOnProperty for default implementation and combination of @Profile and @ConditionalOnMissingBean for dev implementation.

@Service
@ConditionalOnProperty(prefix = "keystore", value = "file")
public class DefaultKeyStoreService implements KeyStoreService {

@Service
@Profile("dev")
@ConditionalOnMissingBean(KeyStoreService.class)
public class DevKeyStoreService implements KeyStoreService {

Now, the problem is in test DevKeyStoreServiceTest for DevKeyStoreService. I have configuration like this:

@SpringBootTest(
    classes = {DevKeyStoreService.class},
    properties = {"keystore.file="}
)
@RunWith(SpringRunner.class)
@ActiveProfiles("dev")
public class DevKeyStoreServiceTest {

    @Autowired
    private DevKeyStoreService tested;

    @Test
    public void testPrivateKey() {
    } //... etc.

The result is:

Negative matches:
-----------------
DevKeyStoreService:
   Did not match:
      - @ConditionalOnMissingBean (types: service.crypto.KeyStoreService; SearchStrategy: all) found bean 'devKeyStoreService' (OnBeanCondition)

and the typical org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'service.crypto.DevKeyStoreServiceTest'....

How can I configure the test class to be able to run it?

2 Answers

Ok, I figured it out.

Annotation with value @ConditionalOnMissingBean(KeyStoreService.class) tries to find only concrete instances, which interface KeyStoreService is not. This way, it doesn't find anything.

When I use annotation with type instead, it works like a charm: @ConditionalOnMissingBean(type = "KeyStoreService").

I think your best bet would to use the name option in @ConditionalOnMissingBean.

The problem you've got now it seems is that the Conditional is find itself and not the missing bean DefaultKeyStoreService.

If you do this

@ConditionalOnMissingBean(name="defaultKeyStoreService")

It will probably work better.

The other option is to specifically have a prod profile to enable the prod bean.

What is interesting is that you can override beans when having a profile if you create those beans with xml.

So if you have in xml

<bean id="x" class="X"/>

And then have

<beans profile="dev">
  <bean id="x" class="Y"/>
</beans>

When you enable the dev profile it will replace the x bean in the unprofiled context with the bean x in the dev profile.

The same behaviour does not occur when using annotations however.

Right when profiles came out I logged a bug in this regard, it wasn't taken up.

Related