I am trying to create a #WebFluxTest in spring to test my controllers.
#WebFluxTest(value = {MyController.class})
public class MyControllerTest {
#MockBean
private MyService service;
#Autowired
private WebTestClient webTestClient;
#Test
void testSomething() {
...
}
However, when I execute the test, I get a lot of: org.springframework.beans.factory.NoSuchBeanDefinitionException for dependencies of #Component s. Meaning that, Spring is trying to find dependencies of #Component when it should ignore those.
I understand that if I use #WebFluxTest, spring should not scan the classpath for any #Component.
My Application class is only annotated with #SpringBootApplication.
What could I be missing here?
SOLUTION UPDATE:
So, I know what was happening. Actually, the class that I had annotated with #Component was an implementation of a WebFilter, and if I check the filter configured for a WebFluxTest (WebFluxTypeExcludeFilter) it adds WebFilter to the include part.
That is why Spring was picking it up.
The error that you're getting could be exactly due to the annotation #WebFluxTest not loading your #Component classes. Could it be that your MyService is instantiating any object that has #Component? Maybe your MyController
Exemplifying, supposing your MyService is like that:
#Service
#AllArgsConstructor
public class MyService {
private final MyRepository repository;
private final Env env;
public void insert(...) {
System.out.println(env.getApplicationName() + " random stuff");
...
}
}
And your Env is annotated with #Component, you will get the same error (NoSuchBeanDefinitionException) if you try to use any method from the Env class since it's null due to #MockBean in MyControllerTest.
The same goes for MyController if it's instantiating any #Component object
If that is the case, then in your MyControllerTest you could try adding #Import(Env.class) or even trying to use when() from mockito with a .thenReturn()
If all that doesn't work, could you please provide more info about your error log and service/controller classes?
So, I know what was happening. Actually, the class that I had annotated with #Component was an implementation of a WebFilter, and if I check the filter configured for a WebFluxTest (WebFluxTypeExcludeFilter) it adds WebFilter to the include part. That is why Spring was picking it up.
It seems that #WebMvcTest and #MockBean are not working as expected. Maybe I'm missing something... I have a controller with some dependencies I'm mocking with #MockBean, but the application fails to start because it cannot find another bean that I think should not be required in this case.
CONTROLLER:
#RestController
public class ExchangeRateStoreController {
private AddExchangeRate addExchangeRate;
private AddExchangeRateRequestAdapter addExchangeRateRequestAdapter;
private GetExchangeRate getExchangeRate;
private GetExchangeRateRequestAdapter getExchangeRateRequestAdapter;
#Autowired
public ExchangeRateStoreController(ExchangeRateRepository exchangeRateRepository, ExchangeRateDateValidator exchangeRateDateValidator, ExchangeRateView exchangeRateView) {
addExchangeRate = new AddExchangeRate(exchangeRateRepository, exchangeRateDateValidator);
addExchangeRateRequestAdapter = new AddExchangeRateRequestAdapter();
getExchangeRate = new GetExchangeRate(exchangeRateView);
getExchangeRateRequestAdapter = new GetExchangeRateRequestAdapter();
}
#PostMapping
#ResponseStatus(HttpStatus.CREATED)
public void create(#RequestBody AddExchangeRateRequest addExchangeRateRequest) {
addExchangeRate.execute(addExchangeRateRequestAdapter.toCommand(addExchangeRateRequest));
}
}
TEST:
#RunWith(SpringRunner.class)
#WebMvcTest(ExchangeRateStoreController.class)
public class ExchangeRateStoreControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
ExchangeRateRepository exchangeRateRepository;
#MockBean
ExchangeRateDateValidator exchangeRateDateValidator;
#MockBean
ExchangeRateView exchangeRateView;
#Test
public void givenValidExchangeRateCommand_whenCreate_thenOK() throws Exception {
String validRequestBody = "{\"from\":\"EUR\",\"to\":\"USD\",\"amount\":1.2345,\"date\":\"2018-11-19\"}";
doNothing().when(exchangeRateDateValidator).validate(any());
doNothing().when(exchangeRateRepository).save(any());
mvc.perform(post("/").content(validRequestBody).contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isCreated());
}
APPLICATION:
#SpringBootApplication
#EnableJpaRepositories("com...exchangerate.store.infrastructure.persistence")
#EntityScan("com...exchangerate.store.infrastructure.persistence")
#ComponentScan(basePackages = {"com...exchangerate.store.infrastructure", "com...exchangerate.store.application"} )
public class ExchangeRateStoreApplication {
public static void main(String[] args) {
SpringApplication.run(ExchangeRateStoreApplication.class, args);
}
}
And the error I get when run the test:
APPLICATION FAILED TO START
Description:
A component required a bean named 'entityManagerFactory' that could
not be found.
Action:
Consider defining a bean named 'entityManagerFactory' in your
configuration.
But, as you can see, entityManagerFactory is not a controller's dependency. So, why is the test trying to load this bean? I'm mocking all the controller dependencies, so I think it shouldn't do this.
The problem's caused by your use of #EnableJpaRepositories on your application's main class. By placing it on the main class, you're indicating that JPA repositories must always be enabled, irrespective of which particular slice of functionality you're trying to test.
You can fix your problem by doing one of the following:
Move #EnableJpaRepositores and #EntityScan onto a separate JPA-specific configuration class
Remove #EnableJpaRepositories and #EntityScan and rely on the auto-configured defaults. For this to work, your repositories and entities will have to be in a sub-package of your main class's package.
There's some more information about this in Spring Boot's reference documentation where it says the following:
If you use a test annotation to test a more specific slice of your application, you should avoid adding configuration settings that are specific to a particular area on the main method’s application class.
In this particular case, the configuration setting that is specific to a particular area is #EnableJpaRepositories.
Hello there is an excellent link on this post: EntityManagerFactory not found in SpringBoot
You could check your spring boot jpa integration and have some good tips to set up your environment.
I am getting the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method setApplicant in webService.controller.RequestController required a bean of type 'com.service.applicant.Applicant' that could not be found.
Action:
Consider defining a bean of type 'com.service.applicant.Applicant' in your configuration.
I have never seen this error before but it's odd that the #Autowire is not working. Here is the project structure:
Applicant Interface
public interface Applicant {
TApplicant findBySSN(String ssn) throws ServletException;
void deleteByssn(String ssn) throws ServletException;
void createApplicant(TApplicant tApplicant) throws ServletException;
void updateApplicant(TApplicant tApplicant) throws ServletException;
List<TApplicant> getAllApplicants() throws ServletException;
}
ApplicantImpl
#Service
#Transactional
public class ApplicantImpl implements Applicant {
private static Log log = LogFactory.getLog(ApplicantImpl.class);
private TApplicantRepository applicantRepo;
#Override
public List<TApplicant> getAllApplicants() throws ServletException {
List<TApplicant> applicantList = applicantRepo.findAll();
return applicantList;
}
}
Now I should be able to just Autowire Applicant and be able to access, however in this case it is not working when I call it in my #RestController:
#RestController
public class RequestController extends LoggingAware {
private Applicant applicant;
#Autowired
public void setApplicant(Applicant applicant){
this.applicant = applicant;
}
#RequestMapping(value="/", method = RequestMethod.GET)
public String helloWorld() {
try {
List<TApplicant> applicantList = applicant.getAllApplicants();
for (TApplicant tApplicant : applicantList){
System.out.println("Name: "+tApplicant.getIndivName()+" SSN "+tApplicant.getIndSsn());
}
return "home";
}
catch (ServletException e) {
e.printStackTrace();
}
return "error";
}
}
------------------------UPDATE 1-----------------------
I added
#SpringBootApplication
#ComponentScan("module-service")
public class WebServiceApplication extends SpringBootServletInitializer {
#Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebServiceApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(WebServiceApplication.class, args);
}
}
and the error went away but nothing happened. However when I commented out everything dealing with Applicant in the RestController prior to adding #ComponentScan() I was able to return a string the UI, thus meaning my RestController was working, now it is being skipped. I ugly Whitelabel Error Page now.
---------------------UPDATE 2------------------------------
I added the base package of the bean it was complaining about. Error reads:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method setApplicantRepo in com.service.applicant.ApplicantImpl required a bean of type 'com.delivery.service.request.repository.TApplicantRepository' that could not be found.
Action:
Consider defining a bean of type 'com.delivery.request.request.repository.TApplicantRepository' in your configuration.
I added #ComponentScan
#SpringBootApplication
#ComponentScan({"com.delivery.service","com.delivery.request"})
public class WebServiceApplication extends SpringBootServletInitializer {
#Override protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
return builder.sources(WebServiceApplication.class);
}
public static void main(String[] args) {
SpringApplication.run(WebServiceApplication.class, args);
}
}
----------------------------Update 3----------------------
adding:
#SpringBootApplication
#ComponentScan("com")
public class WebServiceApplication extends SpringBootServletInitializer {
still is complaining about my ApplicantImpl class which #Autowires my repo TApplicantRepository into it.
It might be because the project has been broken down into different modules.
#SpringBootApplication
#ComponentScan({"com.delivery.request"})
#EntityScan("com.delivery.domain")
#EnableJpaRepositories("com.delivery.repository")
public class WebServiceApplication extends SpringBootServletInitializer {
There is a chance...
You might be missing #Service, #Repository or #Component annotation on your respective implementation classes.
Your Applicant class is not scanned it seems. By default all packages starting with the root as the class where you have put #SpringBootApplication will be scanned.
suppose your main class "WebServiceApplication" is in "com.service.something", then all components that fall under "com.service.something" is scanned, and "com.service.applicant" will not be scanned.
You can either restructure your packages such that "WebServiceApplication" falls under a root package and all other components becomes part of that root package. Or you can include #SpringBootApplication(scanBasePackages={"com.service.something","com.service.application"}) etc such that "ALL" components are scanned and initialized in the spring container.
Update based on comment
If you have multiple modules that are being managed by maven/gradle, all spring needs is the package to scan. You tell spring to scan "com.module1" and you have another module which has its root package name as "com.module2", those components wont be scanned. You can even tell spring to scan "com" which will then scan all components in "com.module1." and "com.module2."
Basically this happens when you have your Class Application in "another package". For example:
com.server
- Applicacion.class (<--this class have #ComponentScan)
com.server.config
- MongoConfig.class
com.server.repository
- UserRepository
I solve the problem with this in the Application.class
#SpringBootApplication
#ComponentScan ({"com.server", "com.server.config"})
#EnableMongoRepositories ("com.server.repository") // this fix the problem
Another less elegant way is to: put all the configuration classes in the same package.
In my case I had a terrible mistake. I put #Service up to the service interface.
To fix it, I put #Service on the implementation of service file and it worked for me.
If a bean is in the same package in which it is #Autowired, then it will never cause such an issue. However, beans are not accessible from different packages by default.
To fix this issue follow these steps :
Import following in your main class:
import org.springframework.context.annotation.ComponentScan;
add annotation over your main class :
#ComponentScan(basePackages = {"your.company.domain.package"})
public class SpringExampleApplication {
public static void main(String[] args) {
SpringApplication.run(SpringExampleApplication.class, args);
}
}
Important:
For anybody who was brought here by googling the generic bean error message, but who is actually trying to add a feign client to their Spring Boot application via the #FeignClient annotation on your client interface, none of the above solutions will work for you.
To fix the problem, you need to add the #EnableFeignClients annotation to your Application class, like so:
#SpringBootApplication
// ... (other pre-existing annotations) ...
#EnableFeignClients // <------- THE IMPORTANT ONE
public class Application {
Side note: adding a #ComponentScan(...) beneath #SpringBootApplication is redundant, and your IDE should flag it as such (IntelliJ IDEA does, at least).
This can also happen if you are using Lombok and you add the #RequiredArgsConstructor and #NonNull for fields but some of your fields are not to be injected in the constructor. This is only one of the possibilities to get the the same error.
parameter 0 required a bean of type MissingBeanName that could not be found
In my case the error told me what Controller the problem was in, after removing #NonNull the application started fine
In my case these two options worked.
in //#ComponentScan ({"myapp", "myapp.resources","myapp.services"})
include also the package which holds the Application.class in the list, or
Simply add #EnableAutoConfiguration; it automatically recognizes all the spring beans.
I faced with familiar problem in my Maven multi-module project with Spring Boot 2. The problem was related to naming of my packages in sub Maven modules.
#SpringBootApplication incapsulate a lots of component like - #ComponentScan, #EnableAutoConfiguration, jpa-repositories, json-serialization and so on. And he places #ComponentScan in com.*******.space package. This part of packages com.*******.space must be common for all modules.
For fixing it:
You should rename all module packages. Other words you had to have in all packages in all Maven modules - the same parent part. For example - com.*******.space
Also you have to move your entry point to this package - com.*******.space
I think you can make it simplified by annotating your repository with #Repository, then it will be enabled automatically by Spring Framework.
It worked for me after adding below annotation in application:
#ComponentScan({"com.seic.deliveryautomation.mapper"})
I was getting the below error:
"parameter 1 of constructor in required a bean of type mapper that could not be found:
Moving the Springbootapplication(application.java) file to another package resolved the issue for me. Keep it separate from the controllers and repositories.
In my case this error appear because my import was wrong, for example, using spring, the import automatically appear:
import org.jvnet.hk2.annotations.Service;
but i needed:
import org.springframework.stereotype.Service;
I faced the same issue. Mongo DB repository was identified by Spring boot, but it was not creating Bean for a repository interface that extended the mongo repository.
The issue in my case was incorrect version specification in maven pom for "spring + mango". I have changed the artifact's group id and it all worked like magic. no annotations needed as spring boot took care of everything.
During my problem resolution, I was all over web searching for solutions and realized that this problem is actually project configuration related, anyone facing this issue should first check their project setup and enable debug from spring to get more details on failure and pay close attention to where exactly in the process, the creation has failed.
I sought online for an answer but it seems there is no one proper solution to my case:
At the very beginning, everything works well as follows:
#Slf4j
#Service
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class GroupService {
private Repository repository;
private Service service;
}
Then I am trying to add a map to cache something and it becomes this:
#Slf4j
#Service
#AllArgsConstructor(onConstructor = #__(#Autowired))
public class GroupService {
private Repository repository;
private Service service;
Map<String, String> testMap;
}
Boom!
Description:
Parameter 4 of constructor in *.GroupService required a bean of type 'java.lang.String' that could not be found.
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
I removed the #AllArgsConstructor(onConstructor = #__(#Autowired)) and add #Autowired for each repository and service except the Map<String, String>. It just works as before.
#Slf4j
#Service
public class SecurityGroupService {
#Autowired
private Repository repository;
#Autowired
private Service service;
Map<String, String> testMap;
}
Hope this might be helpful.
This can happen if the #Service class is marked abstract.
#Configuration annotation will just solve the error
You'll also get this error if you accidentally define the same bean in two different classes. That happened to me. The error message was misleading. When I removed the extra bean, the issue was resolved.
My error was that I had included:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
instead of:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
It might help somebody. I had the same problem, same error message, same everything. I tried solutions from other answers, didn't help until I realised that the bean I am using has the same name as the one that is actually been autowired. It happened in the midst of refactor, thus I had to rename the class, which resulted positively. Cheers
Try configuring the project structure as given below:
Put all the repo, service, packages in the child package of the main package:
package com.leisure.moviemax; //Parent package
#SpringBootApplication
#PropertySource(value={"classpath:conf.properties"})
public class MoviemaxApplication implements CommandLineRunner {
package com.leisure.moviemax.repo; //child package
#Repository
public interface UsrRepository extends JpaRepository<UserEntity,String> {
This error message also pops up when you fail to annotate the Entity classes associated with your bean with the #Entity Annotation.
My ComponentScan worked fine but this popped up for the #repository interface:
#Repository
public interface ExpenseReportAuditRepository extends
PagingAndSortingRepository<ExpenseReportAudit, Integer> {
because I failed to add the #Entity annotation to ExpenseReportAudit
#Entity // <--- Adding this fixed the issue.
public class ExpenseReportAudit {
.....
#SpringBootApplication
#MapperScan("com.developer.project.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
I had a case where i need to inject RestTemplate into a service class. However, the RestTemplate cannot be picked up by the service class. What I did is to create a wrapper class under the same package as main application and mark the wrapper as Component and autowire this component in the service class. Problem solved. hope it also works for you
If your class dependency is managing by Spring then this issue may occur if we forgot to add default/empty arg constructor inside our POJO class.
There is a chance that you are trying to #autowired an interface before implement the interface.
example solution:
**HomeController.java**
class HomeController{
#Autowired
UserService userService;
.....
}
----------------------------------------------------------------------
**UserService.java**
public interface UserService {
User findByUsername(String username);
.....
}
-----------------------------------------------------------------------
**UserServiceImpl.java**
#Service
public class UserServiceImpl implements UserService{
public User findByUsername(String username) {
return userDao.findByUsername(username);
}
....
}
<i>This is not italic</i>, and [this is not a link](https://example.com)
In my case, our project has a Configuration class, so I just added mine like this
#Configuration
public class DependencyConfiguration {
#Bean
public ActivityService activityService(
#Value("${send.money.ms.activity.url}") final String activityHistoryUrl,
final HttpRestService httpRestService
) {
return new ActivityServiceImpl(activityHistoryUrl, httpRestService);
}
.......................
Then the microservice started alright.
PS: I encountered this issue even though the library I need is imported properly and could be seen on External Libraries imported.
Had the same error, transpired it was an issue with the application properties with incorrect username, password and driver and completely unrelated to Bean.
I also received a similar error:
Consider defining a bean of type 'A_REPOSITORY_INTERFACE' in your configuration.
Then, according to Akashe's solution, I added #EnableJpaRepositories to my main class. After that, I received the following error instead:
Consider defining a bean of type 'entityManagerFactory' in your configuration.
Next, I went through all the responses here, googled a lot and read a lot of other resources, which didn't worked out.
Finally, I was lucky to have found the solution on a blog/website (javatute.com). I just followed its examples.
Like suggested by many here, I added #ComponentScan("YOUR_BASE_PACKAGE.*") and #EntityScan("YOUR_BASE_PACKAGE.*") to my main app class, followed by adding a config package and creating a JpaConfig class like:
package YOUR_BASE_PACKAGE.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#Configuration
#EnableJpaRepositories(basePackages = "YOUR_BASE_PACKAGE")
public class JpaConfig {
}
The blog I followed:
Consider defining a bean of type in your configuration
which lead me to:
Error creating bean with name entityManagerFactory defined in class path resource : Invocation of init method failed
and finally to:
Many To Many Mapping In Hibernate/JPA Using Spring Boot And Oracle
For my Spring-Boot app I provide a RestTemplate though a #Configuration file so I can add sensible defaults(ex Timeouts). For my integration tests I would like to mock the RestTemplate as I dont want to connect to external services - I know what responses to expect. I tried providing a different implementation in the integration-test package in the hope that the latter will override the real implementation , but checking the logs it`s the other way around : the real implementation overrides the test one. How can I make sure the one from the TestConfig is the one used?
This is my config file :
#Configuration
public class RestTemplateProvider {
private static final int DEFAULT_SERVICE_TIMEOUT = 5_000;
#Bean
public RestTemplate restTemplate(){
return new RestTemplate(buildClientConfigurationFactory());
}
private ClientHttpRequestFactory buildClientConfigurationFactory() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
factory.setReadTimeout(DEFAULT_SERVICE_TIMEOUT);
factory.setConnectTimeout(DEFAULT_SERVICE_TIMEOUT);
return factory;
}
}
Integration test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = TestConfiguration.class)
#WebAppConfiguration
#ActiveProfiles("it")
public abstract class IntegrationTest {}
TestConfiguration class:
#Configuration
#Import({Application.class, MockRestTemplateConfiguration.class})
public class TestConfiguration {}
And finally MockRestTemplateConfiguration
#Configuration
public class MockRestTemplateConfiguration {
#Bean
public RestTemplate restTemplate() {
return Mockito.mock(RestTemplate.class)
}
}
Since Spring Boot 1.4.x there is an option to use #MockBean annotation to fake Spring beans.
Reaction on comment:
To keep context in cache do not use #DirtiesContext, but use #ContextConfiguration(name = "contextWithFakeBean") and it will create separate context, while it will keep default context in cache. Spring will keep both (or how many contexts you have) in cache.
Our build is this way, where most of the tests are using default non-poluted config, but we have 4-5 tests that are faking beans. Default context is nicely reused
1.
You can use #Primary annotation:
#Configuration
public class MockRestTemplateConfiguration {
#Bean
#Primary
public RestTemplate restTemplate() {
return Mockito.mock(RestTemplate.class)
}
}
BTW, I wrote blog post about faking Spring bean
2.
But I would suggest to take a look at Spring RestTemplate testing support. This would be simple example:
private MockRestServiceServer mockServer;
#Autowired
private RestTemplate restTemplate;
#Autowired
private UsersClient usersClient;
#BeforeMethod
public void init() {
mockServer = MockRestServiceServer.createServer(restTemplate);
}
#Test
public void testSingleGet() throws Exception {
// GIVEN
int testingIdentifier = 0;
mockServer.expect(requestTo(USERS_URL + "/" + testingIdentifier))
.andExpect(method(HttpMethod.GET))
.andRespond(withSuccess(TEST_RECORD0, MediaType.APPLICATION_JSON));
// WHEN
User user = usersClient.getUser(testingIdentifier);
// THEN
mockServer.verify();
assertEquals(user.getName(), USER0_NAME);
assertEquals(user.getEmail(), USER0_EMAIL);
}
More examples can be found in my Github repo here
The Problem in your configuration is that you are using #Configuration for your test configuration. This will replace your main configuration. Instead use #TestConfiguration which will append (override) your main configuration.
46.3.2 Detecting Test Configuration
If you want to customize the primary configuration, you can use a
nested #TestConfiguration class. Unlike a nested #Configuration class,
which would be used instead of your application’s primary
configuration, a nested #TestConfiguration class is used in addition
to your application’s primary configuration.
Example using SpringBoot:
Main class
#SpringBootApplication() // Will scan for #Components and #Configs in package tree
public class Main{
}
Main config
#Configuration
public void AppConfig() {
// Define any beans
}
Test config
#TestConfiguration
public void AppTestConfig(){
// override beans for testing
}
Test class
#RunWith(SpringRunner.class)
#Import(AppTestConfig.class)
#SpringBootTest
public void AppTest() {
// use #MockBean if you like
}
Note: Be aware, that all Beans will be created, even those that you override. Use #Profile if you wish not to instantiate a #Configuration.
#MockBean and bean overriding used by the OP are two complementary approaches.
You want to use #MockBean to create a mock and forget the real implementation : generally you do that for slice testing or integration testing that doesn't load some beans which class(es) you are testing depend on and that you don't want to test these beans in integration.
Spring makes them by default null, you will mock the minimal behavior for them to fulfill your test.
#WebMvcTest requires very often that strategy as you don't want to test the whole layers and #SpringBootTest may also require that if you specify only a subset of your beans configuration in the test configuration.
On the other hand, sometimes you want to perform an integration test with as many real components as possible, so you don't want to use #MockBean but you want to override slightly a behavior, a dependency or define a new scope for a bean, in this case, the approach to follow is bean overriding :
#SpringBootTest({"spring.main.allow-bean-definition-overriding=true"})
#Import(FooTest.OverrideBean.class)
public class FooTest{
#Test
public void getFoo() throws Exception {
// ...
}
#TestConfiguration
public static class OverrideBean {
// change the bean scope to SINGLETON
#Bean
#Scope(ConfigurableBeanFactory.SINGLETON)
public Bar bar() {
return new Bar();
}
// use a stub for a bean
#Bean
public FooBar BarFoo() {
return new BarFooStub();
}
// use a stub for the dependency of a bean
#Bean
public FooBar fooBar() {
return new FooBar(new StubDependency());
}
}
}
With #Primary annotation, Bean overriding works with Spring Boot 1.5.X but fails with Spring Boot 2.1.X it throw error:
Invalid bean definition with name 'testBean' defined in sample..ConfigTest$SpringConfig:..
There is already .. defined in class path resource [TestConfig.class]] bound
Please add below properties= which will instruct Spring explicitly to allow overriding, it is self explainatory.
#SpringBootTest(properties = ["spring.main.allow-bean-definition-overriding=true"])
UPDATE: You can add the same property in application-test.yml (file name depend upon what test profile name you are tests with)
Getting a little deeper into it, see my second answer.
I solved the Problem using
#SpringBootTest(classes = {AppConfiguration.class, AppTestConfiguration.class})
instead of
#Import({ AppConfiguration.class, AppTestConfiguration.class });
In my case the Test is not in the same package as the App. So I need to specify the AppConfiguration.class (or the App.class) explicit. If you use the same package in the test, than I guess you could just write
#SpringBootTest(classes = AppTestConfiguration.class)
instead of (not working)
#Import(AppTestConfiguration.class );
It is pretty wired to see that this is so different. Maybe some one can explain this. I could not find any good answers until now. You might think, #Import(...) is not picked up if #SpringBootTestsis present, but in the log the overriding bean shows up. But just the wrong way around.
By the way, using #TestConfiguration instead #Configuration also makes no difference.
I´ve declared an inner configuration class within my test because I wanted to overwrite just a single method
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FileNotificationWebhookTest{
public static class FileNotificationWebhookTestConfiguration {
#Bean
#Primary
public FileJobRequestConverter fileJobRequestConverter() {
return new FileJobRequestConverter() {
#Override
protected File resolveWindowsPath(String path) {
return new File(path);
}
};
}
}
}
However,
Declaring the configuration in #SpringBootTest did not work:
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,classes = {FileNotificationWebhookTest.FileNotificationWebhookTestConfiguration.class})
or annotating the test configuration with #Configuration did not work:
#Configuration
public static class FileNotificationWebhookTestConfiguration {
}
and was leading to
Caused by: org.springframework.context.ApplicationContextException:
Unable to start web server; nested exception is
org.springframework.context.ApplicationContextException: Unable to
start ServletWebServerApplicationContext due to missing
ServletWebServerFactory bean.
What did work for me ( contrary to some other posts here) was using #Import
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#Import(FileNotificationWebhookTest.FileNotificationWebhookTestConfiguration.class)
class FileNotificationWebhookTest {
}
Using Spring: 5.3.3 with Spring-Boot-Starter: 2.4.2
#MockBean creates Mockito mock instead of production build.
If you do not want to use Mockito, but provide a replacement in some other way (i.e. by disabling some features of bean with feature toggles), I suggest using combination of #TestConfiguration (since Spring Boot 1.4.0) and #Primary annotation.
#TestConfiguration will load your default context and apply your #TestConfiguration piece in addition to it. Adding #Primary will force your mocked RestTemplate to be injected to it's dependents.
See simplified example below:
#SpringBootTest
public class ServiceTest {
#TestConfiguration
static class AdditionalCfg {
#Primary
#Bean
RestTemplate rt() {
return new RestTemplate() {
#Override
public String exec() {
return "Test rest template";
}
};
}
}
#Autowired
MyService myService;
#Test
void contextLoads() {
assertThat(myService.invoke()).isEqualTo("Test rest template");
}
}
This is super weird.
In my case, (Spring Boot 2.6.7), I could simply #Import MyTestConfiguration containing a custom #Primary #Bean into my #SpringBootTest, and everything worked.
Right until I needed to explicitly name my bean.
Then I suddenly had to resort to
#SpringBootTest(
properties = ["spring.main.allow-bean-definition-overriding=true"],
classes = [MyTestConfig::class],
)
Check this answer along with others provided in that thread.
It's about overriding bean in Spring Boot 2.X, where this option was disabled by default. It also has some ideas about how to use Bean Definition DSL if you decided to take that path.
The simplest solution I found was to set this property in application.properties:
spring.main.allow-bean-definition-overriding=true
This will enable overriding of beans.
Next, create a configuration class in test, and annotate your bean with:
#Bean
#Primary
This way, this bean will override your usual bean when running tests.
I'm working with spring-boot on a multi module project (maven). Each module has it's own #Configuration class. Basically I do have the following layout
Module foo-embedded (runs just calls the SpringApplication.run()) method:
#Configuration
#EnableAutoConfiguration
#ComponentScan("de.foobar.rootpackage")
#Import({ApplicationConfig.class, RepositoryConfig.class, SecurityConfig.class})
public class FooApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(FooApplication.class, args);
}
}
Module foo-common (contains all beans and spring-data-jpa initialization config)
#Configuration
#EnableJpaRepositories
#EnableTransactionManagement(entityManagerFactoryRef="entityManagerFactory")
public class RepositoryConfig {
#Bean(destroyMethod = "shutdown")
public DataSource getDataSource() {
// returning a Hikari CP here
}
#Bean(name = "entityManagerFactory") // overriding spring boots default
public EntityManagerFactory getEntityManagerFactory() {
// returning a new LocalEntityManagerFactoryBean here
}
}
Module foo-security (containing spring-securiy configuration and related domain classes), which has a maven dependency on foo-common
#Configuration
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {
// configuring HTTP security and defining my UserDetailsService Bean
}
When I start the application using the FooApplication class, everything works as expected. The above mentioned UserDetailsServiceImpl get's autowired with my UserRepository which is being created through the #EnableJpaRepositories annotation.
Since I want to write some integration tests I've added a test clss to one of my modules.
Module foo-media (containing some domain related stuff plus test cases for that module)
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {RepositoryConfig.class, SecurityConfig.class})
#WebAppConfiguration
#IntegrationTest
public class DirectoryIntegrationTest {
// my test code
}
When I run the test it seems that the SecurityConfiguration get's loaded before the RepositoryConfig.class does. Since the security config defined the UserServiceImpl which must be autowired, the test fails to start with a
NoSuchBeanDefinitionException telling me: No qualifying bean of type [com.foo.rootpackage.security.repository.UserRepository]
I already tried to add #DependsOn("UserRepository") at the bean definition of UserDetailsService, telling me that spring can't find a bean by that name.
Any hints or help would be greatly appreciated! Thanks in advance!
---- EDIT (since I was asked to provide more code) ----
For testing I do not use the actual RepositoryConfig.class, but have a TestRepositoryConfig.class in the common module. Looking like this
#Configuration
#EnableTransactionManagement
#EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory", basePackages = "de.foobar.rootpackage")
public class TestRepositoryConfig extends RepositoryConfig {
#Bean
#Override
public DataSource getDataSource() {
// returning the ds for testing
}
}
You can use #Order annotation on your configuration classes to define load ordering. But it's strange because Spring should resolve proper order - so please check if you property inject UserRepository in UserDetailsService
So I was able to solve this. As it pointed out it had nothing to do with the loading order of the configuration classes (which was my first thought).
As you may notice, the only Configuration that had a #ComponentScan annotation was the FooApplication.class
Spring was not able to find the Repositories, as it didn't know where to look for. Providing the basePackages attribute like this:
#EnableJpaRepositories(basePackages = "de.foobar.rootpackage")
at the TestRepositoryConfig.class did the trick here.