Create an account

Very important

  • To access the important data of the forums, you must be active in each forum and especially in the leaks and database leaks section, send data and after sending the data and activity, data and important content will be opened and visible for you.
  • You will only see chat messages from people who are at or below your level.
  • More than 500,000 database leaks and millions of account leaks are waiting for you, so access and view with more activity.
  • Many important data are inactive and inaccessible for you, so open them with activity. (This will be done automatically)


Thread Rating:
  • 218 Vote(s) - 3.59 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Spring boot 2.1 bean override vs. Primary

#1
With [Spring Boot 2.1 bean overriding is disabled][1] by default, which is a good thing.

However I do have some tests where I replace beans with mocked instances using Mockito. With the default setting Tests with such a configuration will fail due to bean overriding.

The only way I found worked, was to enable bean overriding through application properties:

spring.main.allow-bean-definition-overriding=true
However I would really like to ensure minimal bean definition setup for my test configuration, which would be pointed out by spring with the overriding disabled.

The beans that I am overriding are either

- Defined in another configuration that imported into my test configuration
- Auto-discovered bean by annotation scanning

What I was thinking should work in the test configuration overriding the bean and slap a `@Primary` on it, as we are used to for data source configurations. This however has no effect and got me wondering: Is the `@Primary` and the disabled bean overriding contradictory?

Some example:

package com.stackoverflow.foo;
@Service
public class AService {
}

package com.stackoverflow.foo;
public class BService {
}

package com.stackoverflow.foo;
@Configuration
public BaseConfiguration {
@Bean
@Lazy
public BService bService() {
return new BService();
}
}

package com.stackoverflow.bar;
@Configuration
@Import({BaseConfiguration.class})
public class TestConfiguration {
@Bean
public BService bService() {
return Mockito.mock(BService.class);
}
}
[1]:

[To see links please register here]

Reply

#2
It is allowed to override @Component with @Bean by default. In your case

@Service
public class AService {
}

@Component
public class BService {
@Autowired
public BService() { ... }
}

@Configuration
@ComponentScan
public BaseConfiguration {
}

@Configuration
// WARNING! Doesn't work with @SpringBootTest annotation
@Import({BaseConfiguration.class})
public class TestConfiguration {
@Bean // you allowed to override @Component with @Bean.
public BService bService() {
return Mockito.mock(BService.class);
}
}
Reply

#3
Overriding beans means that there may be only one bean with a unique name or id in the context. So you can provide two beans in the following way:

package com.stackoverflow.foo;
@Configuration
public class BaseConfiguration {
@Bean
@Lazy
public BService bService1() {
return new BService();
}
}

package com.stackoverflow.bar;
@Configuration
@Import({BaseConfiguration.class})
public class TestConfiguration {
@Bean
public BService bService2() {
return Mockito.mock(BService.class);
}
}

If you add `@Primary` then primary bean will be injected by default in:

@Autowired
BService bService;
Reply

#4
`spring.main.allow-bean-definition-overriding=true` can be placed in test configurations. If you need extensive integration testing, you will need to override beans at some point. It's inevitable.

Though the correct answer has already been provided, it implies that your bean will have different names. So, technically, it's not an override.

If you need a real override (because you use `@Qualifiers`, `@Resources` or something similar), since Spring Boot 2.X is only possible using the `spring.main.allow-bean-definition-overriding=true` property.

**Update:**
Be careful with Kotlin Bean Definition DSL. In Spring Boot it will require a custom ApplicationContextInitializer, like so:

class BeansInitializer : ApplicationContextInitializer<GenericApplicationContext> {

override fun initialize(context: GenericApplicationContext) =
beans.initialize(context)

}
Now if you decide to override one of such DSL-based beans in your test via `@Primary @Bean` method, it will not do. The initializer will kick in after `@Bean` methods and you'd still get the initial, DSL-based bean in your tests even with `@Primary` on the test `@Bean`.
One other option would be to also create a test initializer for your tests and list them all in your test properties, like so(order matters):

context:
initializer:
classes: com.yuranos.BeansInitializer, com.yuranos.TestBeansInitializer
Bean Definition DSL also supports primary property via:

bean(isPrimary=true) {...}
- which you'll need to eliminate ambiguity when you try to inject a bean, **however `main:allow-bean-definition-overriding: true` is not needed if you go pure DSL way.**

(Spring Boot 2.1.3)
Reply

#5
I make the testing beans available only in `test` profile, and allow overriding for just while testing, like this:

```
@ActiveProfiles("test")
@SpringBootTest(properties = {"spring.main.allow-bean-definition-overriding=true"})
class FooBarApplicationTests {

@Test
void contextLoads() {}
}

```

The bean I am mocking in the test configuration:

```
@Profile("test")
@Configuration
public class FooBarApplicationTestConfiguration {
@Bean
@Primary
public SomeBean someBean() {
return Mockito.mock(SomeBean.class);
}
}

```

Reply



Forum Jump:


Users browsing this thread:
1 Guest(s)

©0Day  2016 - 2023 | All Rights Reserved.  Made with    for the community. Connected through