Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Annotation @ConditionalOnMissingBean not matching in test

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?

like image 576
JiKra Avatar asked Dec 21 '18 10:12

JiKra


People also ask

What is the @bean annotation?

@Bean is a method-level annotation and a direct analog of the XML <bean/> element. The annotation supports most of the attributes offered by <bean/> , such as: init-method , destroy-method , autowiring , lazy-init , dependency-check , depends-on and scope .

What is@ ConditionalOnMissingBean?

The @ConditionalOnMissingBean annotation is a spring conditional annotation for registering beans only when they are not already in the application context.

What is proxyBeanMethods?

proxyBeanMethods allows you to invoke one method marked as Bean from another one declared in the same configuration class.

What is @ConditionalOnBean?

The @ConditionalOnBean annotation let a bean be included based on the presence of specific beans. By default Spring will search entire hierarchy ( SearchStrategy.

What is conditionalonmissingbean annotation in Spring Boot?

The @ConditionalOnMissingBean annotation is used to load a bean only if a given bean is missing: The above bean will get loaded by Spring only if there is no other bean of this type present in the context.

What does @conditionalonmissingbean mean?

Note that @ConditionalOnMissingBean is not specifying anything at all. There are three such cases in the file. You don't actually need to specify a bean or a type as it should be deduced from the method's return type. In this case that means that it's conditional on there being no IRuntimeConfig bean.

How do you add a condition to a bean?

Only if the specified condition is satisfied then only bean will be added to the application context. To declare a condition, you can use any of the @Conditional… annotations. The @ConditionalOnMissingBean annotation lets a bean be included based on the absence of specific beans.

How to use @conditionalonmissingbean with multiple springservice names?

For multiple names you can use @ConditionalOnMissingBean (name = { "springService", "anotherSpringService" }). You can use by search and for this to work you have to use any of the following SearchStrategy.


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").

like image 77
JiKra Avatar answered Oct 26 '22 03:10

JiKra


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.

like image 25
Michael Wiles Avatar answered Oct 26 '22 05:10

Michael Wiles