#ComponentScan takes no effect - spring

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)})

Related

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

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?

Spring Java Config: configure already existing bean

I want to configure in my #Configuration class bean which is already created by other library's autoconfiguration. I just need to change some fields in that class after it's being initialized.
But I can't find a proper way how to provide code block in #Configuration class and not using #Bean annotation. Is there an ideomatic way to do so in spring?
One way to do this:
#Configuration
class TestConfig {
#Autowired
private SomeBean someBean;
#PostConstruct
private void initSomeBean() {
// someBean.setProperty("qwe");
}
}
#PostConstruct annotation defines init-method, which is getting called after SomeBean is autowired. In this method you can adjust your bean
Do you want to import one Config in AnotherConfig on? It can be done via annotation placed on AnotherConfig:
import org.springframework.context.annotation.Import;
...
#Import(value = {Config.class})
public class AnotherConfig ... {}

#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: What happens when we move #ComponentScan to another class in the package?

I have the following classes:
#ComponentScan
public class CDPlayerConfig {
#Autowired
private static CompactDisc cd;
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
CompactDisc cd = context.getBean(CompactDisc.class);
System.out.println(cd);
}
}
public interface CompactDisc {
void play();
}
#Component
public class SgtPeppers implements CompactDisc {
public void play() {
System.out.println("play song....");
}
}
When I run the class CDPlayerConfig, the program runs successfully. However, if I remove the ComponentScan annotation to CompactDisc interface or SgtPeppers I get the following error:
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'CompactDisc' available
I think that ComponentScan annotation marks the package where Spring looks for beans. CDPlayerConfig, CompactDisc and SgtPeppers are all placed in the same package, so allegedly moving ComponentScan annotation from one class to another should not make a difference.
If so, why do I get an error?
For #ComponentScan to work you have to "tell" spring where to search, or it must find it with help of other, already loaded, #ComponentScan annotated class (your class must be then annotated also with #Component, #Configuration etc. so it could be found).
In your case, you register application context in the first line of main method - you have specified there to load CDPlayerConfig.class which is #ComponentScan annotated so now spring can automatically find other beans in the package:
ApplicationContext context =
new AnnotationConfigApplicationContext(CDPlayerConfig.class);
If you want to move #ComponentScan to another class, you have to change class registered in AnnotationConfigApplicationContext to some #ComponentScan annotated class:
SgtPeppers:
#Component
#ComponentScan
public class SgtPeppers implements CompactDisc {
(...)
Main in CDPlayerConfig:
ApplicationContext context =
new AnnotationConfigApplicationContext(SgtPeppers.class);
Note you should register context from concrete classes (not interfaces).
Also, above sample would work even without #ComponentScan annotation on SgtPeppers, but then beans defined in other classes from the package wouldn't be found.

Use #Primary to precede an #Component/#Service annotated class

In my project I have a service with the #Service annotation.
#Service
public class ExampleService { ... }
I would like to override this service with a Mockito-mock, using a configuration file for my test in the following way:
public class TestContext{
#Bean
#Primary
public ExampleService exampleService(){
return mock(ExampleService.class);
}
}
and in my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { WebContext.class, TestContext.class})
#WebAppConfiguration
public class TestExample{...}
For some reason the mocks aren't injected. The only way I can make this work is when I don't use #Service (#Component gives the same problem) but use a bean annotated method in the WebContext to load the ExampleService, and I put the TestClass behind the WebContext.class in the ContextConfiguration annotation (like in the code I wrote here). I don't get why, and I would like to know how I can keep using the #Service annotation.

Resources