ContextConfiguration(classes=...) in Test doesn't scan packages specified in the Configuration class - spring-boot

I have my application class defined as
#SpringBootApplication(scanBasePackages = {"com.binance.bot", "com.binance.api.client", "com.gateiobot"})
#Configuration
#EnableScheduling
public class BinancebotApplication implements CommandLineRunner {
And in my springboot test I tried to use the auto scan packages by specifying the above Application class, as:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest
#EnableConfigurationProperties
public class MACDCalculationIntegrationTest {
Test fails with
Parameter 1 of constructor in com.gateiobot.macd.MACDCalculation required a bean of type 'com.binance.bot.common.Mailer' that could not be found.
Why isn't the autoscan working?

Related

Spring boot not scanning components and configurations

I have a multi-module project:
TOP_LEVEL
|-> core
|-> assetManager
'-> requestManager
So, I have a core module which has Application class in the core module.
In my assetManger build.gradle I compile(project(:core))
The application main class is in package : com.test.companydomain.core of the core module.
This Application class is annotated with
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.test.companydomain"})
#EntityScan(basePackages = {"com.test.companydomain", "com.test.companydomain.assetManager"})
#EnableJpaRepositories
class Application {
}
There is a class ClientUtils in assetManager module in the package : com.test.domain.assetManager.server.common.utils;
annotated with :
#Slf4j
#Configuration
#Component("clientUtils")
There are beans that I am creating in this class and It uses other configuration classes for autowiring and creating beans.
The beans are not getting generated as of now from this ClientUtils class.
What can be a possible issue with this?
The error i see is
APPLICATION FAILED TO START
Description:
Field locationService in com.test.companydomain.assetManager.server.vendor.converter.ExternalVendorPojoConversionHelper required a bean of type
'com.test.companydomain.assetManager.server.common.utils.client.LocationService' that could not be found.
This class LocationService is also annotated with #Component for spring to create its bean.
In your application , the main class is present in the package com.test.companydomain.core and by default springboot scans all classes and packages under the current package of the main application for autowiring beans. So , you have provided the annotation #ComponentScan to explicitly tell spring to scan other packages as well.But your util class is in the package com.test.domain.assetManager.server.common.utils , which is not included in the #ComponentScan annotation, so it is not taken up for component scanning.
Could you try adding the package com.test.domain to the component scan in main class like :
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.test.companydomain","com.test.domain"})
#EntityScan(basePackages = {"com.test.companydomain", "com.test.companydomain.assetManager","com.test.domain"})
#EnableJpaRepositories
class Application {
}
If you are using Spring Boot, you should consider using #SpringBootApplication annotation and configure base packages to scan with scanBasePackages instead of #ComponentScan:
package com.test.companydomain.core;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = "com.test.companydomain")
#EntityScan({"com.test.companydomain", "com.test.companydomain.assetManager"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
By default, Spring Boot scans only package of the class annotated with #SpringBootApplication and its sub-packages.
Also, #Configuration is meta-annotated with #Component, so #Configuration classes are candidates for component scanning and should not be explicitly annotated with #Component:
package com.test.domain.assetManager.server.common.utils;
#Configuration
public class ClientUtils {
#Autowired
private ClientProperties properties;
#Bean
public TestClient testClient() {
return new TestClient(properties); //example
}
}
and
package com.test.companydomain.assetManager.server.common.utils.client;
#Component
public class LocationService {
//...
}

How to exclude classes with #Configuration in #SpringBootApplication testing

I am using a dependent module called spring-cloud-aws. It has a #Configuration class as org.springframework.cloud.aws.messaging.config.annotation.SqsConfiguration
In my SpringBoot JUnit test case the SqsConfiguration class is getting detected and Beans are getting initialized. I want to exclude this Configuration in class in my JUNit test case. How to achieve this ?
I tried using #ComponentScan it didn't work.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = SQLTestConfig.class)
#ActiveProfiles("test")
public class BusinessManagerTest {
}
#TestConfiguration
#ComponentScan(basePackages = {"package1","package1"},
excludeFilters = {#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = SqsConfiguration.class)})
#Profile("test")
class SQLTestConfig {
#Bean
public SomeBean beans() {
return new SomeBean();
}
}
Loading this configuration class requires aws credentials to be available. I don't want to inject credentials for running a simple Bean test case.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'simpleMessageListenerContainer' defined in class path resource [org/springframework/cloud/aws/messaging/config/annotation/SqsConfiguration.class]: Invocation of init method failed; nested exception is com.amazonaws.services.sqs.model.AmazonSQSException: The security token included in the request is expired
There are multiple ways to exclude specific auto-configuration during testing:
exclude via properties in your application-test.properties
spring.autoconfigure.exclude=org.springframework.cloud.aws.messaging.config.annotation.SqsConfiguration
exclude via #TestPropertySource:
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#SpringBootTest(classes = SQLTestConfig.class)
#TestPropertySource(properties ="spring.autoconfigure.exclude=org.springframework.cloud.aws.messaging.config.annotation.SqsConfiguration")
exclude via #EnableAutoConfiguration, e.g.:
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#SpringBootTest(classes = SQLTestConfig.class)
#EnableAutoConfiguration(exclude=SqsConfiguration.class)
Choose one that suites you better ;)
So to disable the auto-loading of all Beans for a Test, the test class can explicitly mention the dependencies required. This can be done using ContextConfiguration annotation. eg,
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {EmployeeService.class})
public class EmployeeLeavesTest {
#Autowired
private EmployeeService employeeService;
}
In this eg, only EmployeeService class will be available and other beans will not be loaded.

#EnableAutoConfiguration on AbstractIntegrationTest possible?

Having lots of Integration-Test Implementations like this:
// no #Annotations at all
class SomeIntegrationTest extends AbstractIntegrationTest {
...
}
using (Spring Boot 1.5, JUnit 5)
#SpringBootTest(classes = {CoreConfiguration.class, RestTemplateAutoConfiguration.class, JacksonAutoConfiguration.class})
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#Transactional
public abstract class AbstractIntegrationTest {
...
}
this is always failing with
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'javax.persistence.EntityManagerFactory' available
unless I annotate every IntegrationTest-Implementation with
#EnableAutoConfiguration
class SomeIntegrationTest extends AbstractIntegrationTest {
...
}
I wonder why I cannot #EnableAutoConfiguration the AbstractIntegrationTest and be done with it.
(When doing so, it fails with IllegalArgumentException: No auto-configuration attributes found. Is package.SomeIntegrationTest annotated with EnableAutoConfiguration?)
Our normal Apps look like this:
#SpringBootApplication
#Import({CoreConfiguration.class, OtherConfiguration.class})
public class WebApp {
here the #SpringBootApplication obviously implies #EnableAutoConfiguration but I would like to avoid annotating each and every *IntegrationTest with this and instead configure it once on the AbstractIntegrationTest.
Is this fighting against spring-boot in any way or is there some way to achieve this? Thanks.
You could create update your AbstractIntegrationTest abstract class to have a small inner configuration class e.g. TestConfiguration which is loaded using the #Import(TestConfiguration.class) annotation.
#SpringBootTest(classes = {CoreConfiguration.class, RestTemplateAutoConfiguration.class, JacksonAutoConfiguration.class})
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#Transactional
#Import(AbstractIntegrationTest.TestConfiguration.class) // <---- import the configuration
public abstract class AbstractIntegrationTest {
#EnableAutoConfiguration
// Any other applicable annotations e.g. #EntityScan
static class TestConfiguration {
}
....
}

spring boot test slices for Web applications

I have an SpringBootApplication annotated class that also extends WebSecurityConfigurerAdapter to provider websecurity. This application also has a JPA layer which I want to test in isolation.
The appliation class roughly looks like
#SpringBootApplication
public class Application extends WebSecurityConfigurerAdapter {
...
}
Now I have a repository test that looks roughly like
#TestExecutionListeners({DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class, DbUnitTestExecutionListener.class})
#RunWith(SpringJUnit4ClassRunner.class)
#DataJpaTest
#DatabaseSetup(Stubs.MASTER_DATASET)
public class AppUserRepositoryTest {
...
}
However when I run this I get an error that says an ObjectPostProcessor needs to be wired. While I can understand the reason, Trying to understand if there is any workaround/solution to avoid having to define #MockBeans for all of the dependencies in WebSecurityConfigurerAdapter. The error I get is
Description:
Parameter 0 of method setObjectPostProcessor in org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter required a bean of type 'org.springframework.security.config.annotation.ObjectPostProcessor' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.security.config.annotation.ObjectPostProcessor' in your configuration.

#ComponentScan takes no effect

I have the following code:
Implementation of Bean:
package my.persist.services;
#Component
public class MyService{
}
Test:
package my.persist.services;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes={"other configuration classes", my.persist.services.MyService.class})
#ComponentScan(basePackageClasses = {my.persist.services.DummyPlaceHolder.class})
public class MyServiceTest extends AbstractJUnit4SpringContextTests {
#Autowired
MyService service;
}
When I remove "my.persist.services.MyService.class" from #ContextConfiguration, the compiler says "Could not autowire, no bean of ... found", it seems the #ComponentScan has no effect? Any help?
Rather than component scan for individual classes, does a wildcard scan of your base package work?
#ComponentScan(basePackages = {"my.persist.services.*"})
You can exclude certain ones in your test, i.e if you want to filter out your real implementation in your test, you can do the following:
#ComponentScan(basePackages = {"my.persist.services.*"}, excludeFilters={
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=MyService.class)})

Resources