Test class has dependency failure because of conditional Beans - spring-boot

I have few beans which i declared with some condition using #ConditionalOnProperty annotation. I have a test class, which is failing because of autowiring unsatisfied dependency as the conditional bean didnt load. Can you help me to figure out how to execute that whole test class only when the condition is satisfied?
I tried using #BeforeClass and tried to skip the test class execution, with skipException, but that didnt work

Related

What is difference between using and not using #Autowired Constructor in springboot test with kotlin

I wanna write test code using JUnit5 in spring boot 2.3.0.
First of all, I tried dependency injection using constructor.
Like below
But when I started my test code, whole of test was failed.
Error log was No ParameterResolver registered for parameter blah blah blah...
So I thought Bean Injenction is failed even though I was using constructor.
In addition, The Bean Icon didn't show up even though I used the constructor In Intellij IDE.
But when I was added #Autowired constructor at constructor, Bean Icon began to appear.
Like below
Resources Class is a bean which is only uesd in test code.
I could solve the problem but I really didn't understand what happend.
I wanna know What is difference between using and not using #Autowired Constructor
Please let me know how this magical #Autowired Constructor works
#Autowired on the constructor of a test case is a signal to the test framework (in this case to the Jupiter SpringExtension) to resolve constructor parameters using the Spring context. Without the annotation Jupiter tries to find parameter resolvers through other mechanisms but doesn’t find any, hence the error.

Spring Configuration Conditional On Test

I want my #Configuration to not be active during tests with #SpringBootTest.
Is there any configuration property or bean active automatically in every test to detect if the ApplicationContext is in test?
For example:
#Configuration
#ConditionalOnMissingBean(TestEntityManager.class)
But I can't use TestEntityManager because that is only in a #DataJpaTest context.
I want to avoid going into every #SpringBootTest and doing configurations for each.
After some debugging, I think I found a common bean that only exists in the test context:
#ConditionalOnMissingBean(type = "org.springframework.boot.test.mock.mockito.MockitoPostProcessor")

Configuration of SpringJUnit4ClassRunner Test clashes with SpringBootTest

I have a bunch of tests in my project that are all annotated with #SpringBootTest and therefore load up a SpringBoot context.
Now recently I refactored a Test in which I wanted a smaller scope (it´s about process coverage with camunda) to #RunWith(SpringJUnit4ClassRunner.class).
Since this means that no context is loaded automatically I create some beans "manually" with a static inner class configuration. The entire test looks something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {
ExternalConfiguration.class, MyTest.InternalConfiguration.class
})
public class MyTest{
#Autowired
private SomeBean someInternalBean;
#Configuration
public static class InternalConfiguration{
#Bean
SomeBean someInternalBean() {
return mock(SomeBean .class);
}
}
//Tests
Now, this test runs fine when I run it. BUT when I run any other test ( those still annotated with #SpringBootTest), I get issues with when the ApplicationContext is loaded:
The bean 'someInternalBean', defined in class path resource [.../MyTest$InternalConfiguration.class], could not be registered. A bean with that name has already been defined in file [.../SomeBean.class] and overriding is disabled.
Apparently a bean is created when loading the ApplicationContext because the class is annotated with #Component AND the context loader tries to create another bean from my internal configuration.
I cant allow bean-overriding because my mock beans might overwrite the automatically created beans (which they do, I tried).
How do I circumvent this? I want my SpringJUnit4ClassRunner-tests with their internal configurations to not affect my other #SpringBootTest-tests. I already tried making the configuration beans conditional with #ConditionalOnMissingBean but that did not work.
Turns out those inner configuration classes should not be annotated with #Configuration. Removing the annotation makes it so that the manual bean generation still works and the configuration is no longer picked up by the componentScan.

#Qualifier and #Resource doesn't work when running test case under Spring test framework

I have a test case which has a dependency of 'ticketDao', like below:
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Qualifier;
public class LfnSaleCancellationIntegrationTest extends BaseIntegrationTest {
//#Resource(name = "baseTicketDao")
private BaseTicketDao ticketDao;
....
public void setTicketDao(#Qualifier("baseTicketDao") BaseTicketDao ticketDao) {
this.ticketDao = ticketDao;
}
}
and BaseIntegrationTest extends from spring test framework's AbstractJpaTests, Spring is v3.0.5
When run this test case, I got a similar exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No unique bean of type [com.mpos.lottery.te.gamespec.sale.dao.BaseTicketDao]
is defined: expected single matching bean but found 2:
[baseTicketDao, extraballTicketDao]
My project has evolved a long time, in fact when I encountered this exception at the first time, #Qualifier solved it. Till today this project has changed much, but I really have no idea why #Qaulifier and #Resource don't work any more.
And if i remove the dependency of 'ticketDao', the test case will pass. I am wondering whether there are some change of spring configuration cause this exception? or ... i have googled much, but seem no other people ever faced such a problem, pls give your comments, thanks very much!
You are using AbstractJPATests which is part of old spring test framework and (indirect) subclass of AbstractDependencyInjectionSpringContextTests. By default the injection is not annotation based but it discovers setters and fields and attempts injection by type. It would be recommended to switch to newer annotation based tests, refer to spring documentation for details.
As a workaround try to change autowire mode. Call it in test constructor as this.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME), rename your field to baseTicketDao and remove setter.
I knew the reason. In my new project, there are a statement of context:component-scan in spring configuration file, which will register 4 BeanPostProcessors by default:
AutowiredAnnotationBeanPostProcessor(#Autowired)
RequiredAnnotationBeanPostProcessor(#Require)
CommonAnnotationBeanPostProcessor(JSR-250 annotations, #Resource, #PostConstruct etc, #WebServiceRef )
PersistenceAnnotationBeanPostProcessor(#PersistenceUnit and #PersistenceContext)
While in my old project, only the default BeanPostProcessor(internalAutoProxyCreator) has been registered. My understanding is AutowiredAnnotationBeanPostProcessor will always wire by type. Anyway if remove context:component-scan, my test case can pass now.
In fact i have migrate all my test cases to spring test context framework now, and context:component-scan must be stated, otherwise #Autowired, #Resource etc annotation will be ignored, and you will get a great many of NullPointerException of those automaticaly injected dependencies.
NOTE: <context:annotation-config/> will register those 4 BeanPostProcessors too.

conflicts with existing, non-compatible bean definition of same name and class after proguard obfuscation

after Proguard obfuscation i get the following error :
Unexpected exception parsing XML document from ServletContext resource
[/WEB-INF/applicationContext.xml]; nested exception is
java.lang.IllegalStateException: Annotation-specified bean name 'a'
for bean class [com.company.project.b.a.a.a] conflicts with existing,
non-compatible bean definition of same name and class
[com.company.project.a.a]
i'm using annotation based spring configuration , how can i avoid having two classes with the same name using Proguard because Spring doesn't allow two beans to have the same name.
I'm not sure if this is what you want, but you can specify bean name in #Component (and stereotypes #Repository, #Service and #Controller) value:
#Component("myBeanName")
public class MyBean {
}
I had the same problem and nothing else was helping out. Sometimes the problem occurs if you have moved your classes around and it refers to old classes, even if they don't exist.
In this case, just do this :
mvn eclipse:clean
mvn eclipse:eclipse
This worked well for me.
Another cause; you may have different versions of Spring in your classpath, for example, spring 2.x with spring 3.x. In such condition, beans seem to be loaded twice. If you use maven, check if a module does not import an old version of Spring (mvn dependency:tree) and remove it by excluding the involved spring artifact (exclusions).

Resources