I have a SpringBoot main/Application.java class
#SpringBootApplication
#ComponentScan(value = "com.nfl.dm.shield", excludeFilters =
{
#ComponentScan.Filter(value = MemoryRepository.class, type = FilterType.ASSIGNABLE_TYPE)
}
)
public class Application {
final static Logger LOG = LoggerFactory.getLogger(Application.class);
public static void main(String[] args) {
LOG.info("Booting application...");
SpringApplication.run(Application.class, args);
}
}
and a similar one for Test
#Configuration
#ComponentScan(basePackages = {"com.nfl.dm.shield"}, excludeFilters =
{
#ComponentScan.Filter(value = MySqlRepository.class, type = FilterType.ASSIGNABLE_TYPE)
}
)
public class ApplicationTestConfig {
}
The main code runs correctly. The test code throws NoUniqueBeanDefinitionException, appearing to not properly filter out the unwanted MySqlRepository component.
After over a day of trying many different ways to exclude the unwanted bean, the core issue turned out to be the fact that #ComponentScan was pulling in both Application and ApplicationTest, resulting in an additional scan for Application, resulting in the unwanted service being loaded.
The solution, add:
#ComponentScan.Filter(value = Application.class, type = FilterType.ASSIGNABLE_TYPE)
to the list in ApplicationTestConfig.java. So, when ApplicationTestConfig is loaded and triggers the component scan, it ignores Application (and all of Application's specific configurations).
Related
Spring Boot 2.3.12 (I can't update to a newer version for reasons out of my control).
I have defined my main application class with specific scan base packages like this:
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
}
)
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}
What I'm trying to accomplish is both:
A) include a package outside the application's base package (hence the org.otherpackage.ComponentScanMarker.class reference in the #SpringBootApplication annotation)
and
B) exclude the HateoasConfiguration class completely*.
I've also tried this:
#SpringBootApplication
#ComponentScan(
basePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
That results in HateoasConfiguration being loaded despite the excludeFilters.
Another option I tried:
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
exclude = HateoasConfiguration.class
)
That results in an exception at startup with the message:
The following classes could not be excluded because they are not auto-configuration classes:
- org.springframework.hateoas.config.HateoasConfiguration
I can't get it to work, no matter what combination of annotation properties I try. Either HateoasConfiguration gets loaded despite the attempt to exclude it, or #Components in org.otherpackage don't get loaded. I've looked at a few different similar questions and answers, but none of them include the need for both goals.
How can I accomplish both needs, to include multiple base packages for component scanning, and exclude a specific #Configuration class that's on the classpath?
* This question really has nothing to do with Spring HATEOAS, it's just an example of a #Configuration class that is on the classpath but I want Spring Boot to ignore. Here are the annotations present on that class (source code here):
#Configuration(proxyBeanMethods = false)
#EnablePluginRegistries({ LinkDiscoverer.class })
public class HateoasConfiguration {
Have you tried this ?
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
exclude = { HateoasConfiguration.class }
)
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}
I've been looking for a solution for a similar problem myself. Dropping it here for future developers.
The underlying problem is that #SpringBootApplication performs a full component scan itself, so your own #ComponentScan annotation does not have the desired effect.
The solution for me was to eject #SpringBootApplication and just do what it does internally (with our desired modifications) as it's just a convenience shorthand.
For your case, try:
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(
basePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
})
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}
I am starting to write an application in spring boot and below is how my package structure looks:
com.practice.spring.project.helloworld.HelloworldApplication.java
com.practice.spring.project.repository.EmployeeRepository.java
com.practice.spring.project.model.Employee.java
Below is how i had my application startup successfully,
#SpringBootApplication
#ComponentScan(basePackages = "com.practice.spring.project.DB", basePackageClasses = InitDatabase.class)
#EnableJpaRepositories(basePackages = "com.practice.spring.project.repository" , basePackageClasses = EmployeeRepository.class)
public class HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloworldApplication.class, args);
}
#Bean
public CommandLineRunner run(EmployeeRepository employeeRepository) throws Exception {
return (args) -> {
System.out.println("Calling it after the application context is all loaded up");
employeeRepository.save(new Employee("Ashwin", "Architect"));
};
}
}
My question is should I have to specify the base-packages & baseClasses for every class I add ? It would be tough if have 10 packages having 10 different classes.
Am sure there should be an easier way to scan and instantiate classes in different package.
Figured out a way - set the basePackages to com.practice.spring.project.*
#ComponentScan(basePackages = "com.practice.spring.project.*")
I believe this is a very particular case, but I am building some cucumber tests for some third-party applications we use.
Since I am not really testing my own application, I created a maven project and configured cucumber to run in the main folder (not the test folder).
This is my entrypoint class:
#SpringBootApplication
public class ExecutableMain implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ExecutableMain.class, args);
}
#Override
public void run(String... args) {
// args logic...
JUnitCore.runClasses(MyCucumberTest.class);
}
}
And my test class:
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {"pretty", "html:target/cucumber", "json:target/cucumber/cucumber.json"},
glue = {"cucumber.app", "cucumber.steps"}
)
public class MyCucumberTest {
#AfterClass
public static void tearDown(){
// quit the browser
}
}
This currently works fine, but I want to add spring features to my tests.
Specifically, I want to autowire something in my cucumber steps.
Stepdefs:
public class MyStepdefs {
#Autowired
private ConfigProperties properties;
#Given("^Something")
public void example() {
//...
}
I searched around and found people saying I should add the ContextConfiguration annotation in the steps. I did it like so:
#ContextConfiguration(classes = ExecutableMain.class, loader = SpringBootContextLoader.class)
public class MyStepdefs {
But this resulted in a loop during start up.
Can I achieve what I need?
Ok, so I got it to work following https://stackoverflow.com/a/37586547/1031162
Basically I changed:
#ContextConfiguration(classes = ExecutableMain.class, loader = SpringBootContextLoader.class)
To:
#ContextConfiguration(classes = ExecutableMain.class, initializers = ConfigFileApplicationContextInitializer.class)
I am not 100% sure how/why it worked, but it did.
I have below test for my spring boot main method.
The test tries to start the application 2 times which is expected.
First time when it starts the application it uses the Mock object hewever 2nd time it starts the application it calls the actual bean.
I have ReferenceDataService having #PostConstract method call which makes rest call to some other application which I don't want in my tests.
Another thing is that MqConfiguration which tries to connect to IBM queues that also I would like to avoid in my test.
Please note even though I have added #ComponentScan(excludeFilters... in my test class it does not exclude it.
How do I write test for my main method in this case?
#ActiveProfiles(profiles = {"test"})
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MainApplication.class, webEnvironment = WebEnvironment.RANDOM_PORT, properties = {
"camel.springboot.java-routes-include-pattern=**/NONE*"})
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, SecurityAutoConfiguration.class})
#DirtiesContext(classMode = ClassMode.AFTER_EACH_TEST_METHOD)
#ComponentScan(excludeFilters = {#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {MqConfiguration.class, ReferenceDataCache.class})})
public class MainApplicationTest {
#MockBean
private MqService mqService;
#MockBean
private ReferenceDataService referenceDataService;
#SpyBean
private ReferenceDataCache cacheSpy;
#Test
public void test() {
Mockito.when(referenceDataService.getCurrencies()).thenReturn(new HashMap<>());
Mockito.when(referenceDataService.getFrequencies()).thenReturn(null);
Mockito.when(referenceDataService.getDayCountTypes()).thenReturn(null);
Mockito.when(referenceDataService.getBusinessDayConverntions()).thenReturn(null);
Mockito.when(referenceDataService.getRateDefinations()).thenReturn(null);
Mockito.when(referenceDataService.getBusinessCalendar()).thenReturn(null);
Mockito.when(referenceDataService.getFinancingTypes()).thenReturn(null);
Mockito.when(referenceDataService.getStaffs()).thenReturn(null);
MainApplication.main(new String[]{});
}
}
MainApplication.java (The class to be tested)
#SpringBootApplication
#EnableJms
#EnableCaching
#AutoConfigureBefore(JmsAutoConfiguration.class)
public class MainApplication {
private static final Logger logger = LoggerFactory.getLogger(MainApplication.class);
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
One could split it into two separate testing parts as we should strive to test a single functionality per test (Single Responsibility Principle). You could model your testing like below:
#Test
public void applicationContextLoadedTest(){
}
#Test
public void applicationStartTest() {
//you can add your mocks as per your required dependencies and requirements
MainApplication.main(new String[] {});
}
Alternatively, if you are allowed to use PowerMockito, then the following link gives you a working example for verifying static invocations.PowerMockito - SpringBoot test
I am experiencing rather strange thing when using Spring Boot. Lets get with it.
I have an app which, when ran from spring-boot:run, loads perfectly fine and I can use my server. However, if I try to run tests (either via launching test from IntelliJ or via surefire plugin) context fails to load.
Issue lies within this class (only relevant part shown):
#RestController
#RequestMapping(
value = "/sa/revisions/"
)
#SuppressWarnings("unchecked")
class RevisionController {
#Autowired
// cant autowire this field
private RepositoryEntityLinks repositoryEntityLinks = null;
/* omitted */
}
And here is my main class:
#EnableAsync
#EnableCaching
#EnableAutoConfiguration
#EnableConfigurationProperties
#Import({
SecurityConfiguration.class,
DataConfiguration.class,
RestConfiguration.class
})
public class SpringAtomApplication {
#Autowired
private DataLoaderManager dataLoaderManager = null;
public static void main(String[] args) {
SpringApplication.run(SpringAtomApplication.class, args);
}
#Bean
public CacheManager cacheManager() {
final GuavaCacheManager manager = new GuavaCacheManager();
manager.setAllowNullValues(false);
return manager;
}
#PostConstruct
private void doPostConstruct() {
this.dataLoaderManager.doLoad();
}
}
As I said, application loads without an issue when ran normally, however when it comes to this simple test, everything falls apart:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = SpringAtomApplication.class)
public class SpringAtomApplicationTests {
#Test
public void contextLoads() {
}
}
Would appreciate any suggestion, because I'd love to start with testing it.
You should set SpringApplicationContextLoader in your test class:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(
classes = SpringAtomApplication.class,
loader = SpringApplicationContextLoader.class)
public class SpringAtomApplicationTests {
#Test
public void contextLoads() {
}
}
With that you can test non-web features (like a repository or a service) or start an fully-configured embedded servlet container and run your tests using MockMvc.
Reference: http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/SpringApplicationContextLoader.html