Spring #ContextHierarchy with custom initializer using addActiveProfile gives NoSuchBeanDefinitionException - spring

We are using #ContextHierarchy in our tests to enable context caching but allow some texts to customise the context. I have a problem that one test needs to activate a specific Spring profile which activates a bean which is injected back into the test.
I have a feeling this is not possible but am looking for confirmation. Logically the parent context has already been initialised, so am willing to believe Spring will have a problem re-wiring the parent context based on a profile being activated in a test that is essentially just adding a child context through #ContextHierarchy. But I couldn't find anyone describing this problem after some googling.
The base test class looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextHierarchy(
#ContextConfiguration(classes = BaseConfig.class, initializers = TestContextInitialiser.class)
)
public abstract class BaseTest extends AbstractTransactionalJUnit4SpringContextTests {
...
}
The concrete test class looks like this:
#ContextHierarchy(
#ContextConfiguration(initializers = NotificationTest.CustomInitializer.class)
)
public class NotificationTest extends BaseTest {
#Autowired
private EventsConfig eventsConfig; // bean that needs to be activated via profile
...
public static class CustomInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getEnvironment().addActiveProfile("EVENTS_ENABLED");
}
}
}
The EventsConfig class uses #Profile...
#Component
#Profile("EVENTS_ENABLED")
public class EventsConfig {
...
}
And the error from Spring:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [xxx.EventsConfig] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
I would expect that Spring would at least warn/error in this situation and explain that the parent context cannot be modified with new profiles.
Any input/guidance appreciated. Is this or something similar possible? It will be a pain to have to refactor this test to avoid use of the base class.
Thanks

Related

Spring #Autowired behavior different in tests than components

Are the rules/behaviors around #Autowired different when writing tests? It seems that with a test, you can autowire to a concrete type, but if you try the same thing inside a #Component it will fail. This is a contrived example, but it's something I ran into and am just trying to understand better.
Contrived example code:
public interface Gizmo {
void whirr();
}
#Configuration
public class GizmoConfiguration {
#Bean
#Profile("no-dependencies")
public Gizmo fooGizmoBean() {
return new FooGizmo();
}
#Bean
#Profile("!no-dependencies")
public Gizmo barGizmoBean() {
return new BarGizmo();
}
public class FooGizmo implements Gizmo {
#Override
public void whirr() {
}
}
public class BarGizmo implements Gizmo {
#Override
public void whirr() {
}
}
}
Test that runs fine:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles(Application.Profiles.NO_DEPENDENCIES)
public class TestClass {
#Autowired
private GizmoConfiguration.FooGizmo gizmo;
#Test
public void test() {
assertNotNull(gizmo);
}
}
Component that causes java.lang.IllegalStateException: Failed to load ApplicationContext:
#Component
public class TestComponent {
#Autowired
private GizmoConfiguration.FooGizmo gizmo;
}
because of:
No qualifying bean of type 'GizmoConfiguration$FooGizmo' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Are the rules/behaviors around #Autowired different when writing
tests?
Not exactly: the rules are actually exactly the same. The difference is in terms of timing with regard to how Spring determines if a given bean is an autowire candidate.
It seems that with a test, you can autowire to a concrete type,
but if you try the same thing inside a #Component it will fail.
I understand why you would think that, since your example demonstrates that behavior, but your analysis is not exactly correct.
So let me explain...
When Spring attempts to perform autowiring for your #Component class, the only information it has about types (i.e., classes and interfaces) for beans coming from #Bean methods is the information available in an #Bean method's formal signature.
In your example, when Spring searches for so-called "autowire candidates" to inject into your #Component, Spring only sees a bean of type Gizmo for your fooGizmoBean() #Bean method. So that's why you see the "No qualifying bean of type 'GizmoConfiguration$FooGizmo'" error, which happens to be completely correct.
If you want Spring to be able to autowire your #Component using the concrete type, you will have to redefine the signature of your fooGizmoBean() #Bean method to return FooGizmo instead of Gizmo.
So, that's the first half of the story. The second half of the story is why the Spring TestContext Framework is able to perform autowiring by the concrete type for the test instance.
The reason that works is that the ApplicationContext has already been completely started (i.e., all beans have been instantiated and all #Bean methods have been invoked by the container) by the time the testing framework attempts to perform dependency injection. By that point in time, the fooGizmoBean() method has already been invoked by Spring, and Spring now knows the concrete type is actually a FooGizmo. Thus, #Autowired FooGizmo gizmo; works in the test.

Test application cannot find autowired bean in same project

I have a jar file that is the persistence layer, ad I just want to test the DAO that are simply autowired into other service layer clasees. But I want to test without any mocking or whatever.
I think this should be pretty simple. I have this in my srs/test/java
#RunWith(SpringRunner.class)
#ComponentScan("com.xxxx")
public class ApplicationTester {
#Autowired
AplicationDocumentDao aplicationDocumentDao;
#Test
private void testAplicationDocumentDao() {
aplicationDocumentDao.allForOrg(1);
}
}
All the DAO's are in the same projust under the usual /src/main/java
When I run the mvn to just run the tests like this:
mvn -Dtest=ApplicationTester test
I get this error:
Error creating bean with name 'xxx.test.ApplicationTester': Unsatisfied dependency expressed through field 'aplicationDocumentDao';
nested exception is 0rg.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'xxx.dao.AplicationDocumentDao' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Which means is can't find the Bean of course, but i would think the #ComponentScan would pick up add the Dao's.
How do I get this tester to find all my Dao's (Which are all annotated with #Componenet) and are picked up just fine in the rest for the application.
Any ideas?
** EDIT **
here is the DAO
#Repository
#Component
public class AplicationDocumentDao extends JdbcDaoSupport {
#Autowired
public void setJT(JdbcTemplate jdbcTemplate) {
setJdbcTemplate(jdbcTemplate);
}
public List<ApplicationDocumentBean> allForOrg(int orgId) {
String sql = "SELECT * FROM ApplicationDocument WHERE organizationId = ?";
return (List<ApplicationDocumentBean>) getJdbcTemplate().query(sql, new BeanPropertyRowMapper<ApplicationDocumentBean>(ApplicationDocumentBean.class), orgId);
}
}
Add annotate to your persistence layer class, for example: #Repository or #Component,like this :
#Repository
public interface OrderMapper {}

POJO Injection in Spring similar to CDI

I have some java objects coming from external library which I need to inject in my spring project. Problem is the classes from library is not aware of any spring api's
If I inject the beans from library to Service using #Autowired I am getting org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
Following is my service class
#Path("/test")
public class TestService {
#Autowired
SomeOtherClass service;
#GET
public Response get(){
return Response.ok(service.someMethod()).build();
}
}
and following is my class from library which is not aware of spring
public class SomeOtherClass {
public String someMethod(){
return "Data from library";
}
}
When I invoke my service I get exception as
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.SomeOtherClass' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Is there are way in spring to inject a plain Java Object similar to that of injection in **CDI**?
There is one option to define applicationcontext.xml and define SomeOtherClass in xml and use getBean, but I don't want to do that. Is there any other option?
Note:
Following options cannot be considered because I have100's of classes coming from library
Cannot use applicationcontext.xml
Cannot #Configuration #Bean to produce beans.
You could use the #Configuration and #Bean annotations as follows -
Create a new class:
#Configuration
public class AppConfig {
#Bean
SomeOtherClass someOtherClassBean(){ return new SomeOtherClass();}
}
Now the auto wiring shall work.
What it does, is actually creating a bean and letting Spring know about it.
Maybe try adding the beans programatically to the IoC container:
Add Bean Programmatically to Spring Web App Context
You need to find all the classes you want to instantiate and use one of the methods in the linked question.
You can use reflection to add Bean definitions programatically.
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Reflections ref = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.setUrls(ClasspathHelper.forPackage(PACKAGE_NAME))
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(PACKAGE_NAME))));
ref.getSubTypesOf(Object.class).stream()
.forEach(clazz -> {
logger.info("Defining pojo bean: {} -> {}", Introspector.decapitalize(clazz.getSimpleName()), clazz.getCanonicalName());
registry.registerBeanDefinition(Introspector.decapitalize(clazz.getSimpleName()),
BeanDefinitionBuilder.genericBeanDefinition(clazz).getBeanDefinition());
});
}
Subsequently, these beans can be #Autowired elsewhere. See Gist: https://gist.github.com/ftahmed/a7dcdbadb8bb7dba31ade463746afd04

Autowire Spring bean implementing two interfaces

Is it possible to have a Spring Bean implement 2 interfaces and be able to autowire that bean using either interface?
I have the following two interfaces:
public interface ServiceA {}
public interface ServiceB {}
Two controllers which use constructor auto-wiring to inject a different service:
#RestController
public class ControllerA {
public ControllerA(ServiceA service) {}
}
#RestController
public class ControllerB {
public ControllerB(ServiceB service) {}
}
One class that implements both the services
#Service
public class ServiceImpl implements ServiceA, ServiceB { }
I am getting a NoSuchBeanDefinitionException:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [ServiceB] found for dependency [ServiceB]: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
I'm using Spring Boot version 1.4.0
Yes it is possible, but
it is important, to create the service bean of type ServiceImpl and not as one of the service interfaces :
#Bean
ServiceImpl service() {
return new Serviceimpl();
}
Spring uses reflection on the declared bean type to find out which interfaces it implements and not on bean.getClass().
Even if this answer was voted dowen, you can be asured : it works . If it does not work for you #scarba05, your problem must be somewhere else...
You could use the #Qualifier annotation. It can be applied alongside #Autowired or #Inject at the point of injection to specify which bean you want to be injected:
#Autowired
#Qualifier("iceCream")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
Source: Spring in Action 4th edition.
Let me answer your questions one by one:
Yes you can implement more than one interface in any spring bean.
Yes you can autowire with interface too as you did by constructor.
Your shared code working fine just check your SpringBootConfiguration class I think you are not scanning you service package or your service class is not in child package of SpringBootConfiguration class.
That's why you are facing:
NoSuchBeanDefinitionException

Spring Test + Mockito.mock - Spring fails because it tries to load the mocked bean #Autowired dependencies

I can't find out why the following simple scenario is failing: I have a Spring application with a filter that loads a Spring bean from the application context:
public class MyFilter implements Filter{
private IPermissionService permissionService;
public void init(FilterConfig filterConfig) throws ServletException {
WebApplicationContext ac = null;
try{
ac = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
permissionService = ac.getBean(PermissionServiceImpl.class);
PermissionServiceImpl has an #Autowired attribute dataSource so in my TestNG test, I mock it in the Spring applicationContext:
#Configuration
public class MyFilterSpringTestConfig{
#Bean
public IPermissionService permissionService(){
return Mockito.mock(PermissionServiceImpl.class);
}
MyTest:
#Test
#WebAppConfiguration
#ContextConfiguration(classes=MyFilterSpringTestConfig.class)
public class MyFilterSpringTest extends BaseSpringFilterTest{
...
The problem is that on Spring initialization I get an exception complaining that PermissionServiceImpl's dataSource dependency is not satisfied. Since I wrapped it with a mock, why is it still failing? How could I fix it?
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=myDataSource)}
When mocking a class using Mockito (or any other mocking framework) that class is still an instance of the original class. With that comes that it also contains all the annotations and class information with it.
So when you create a mock of the class it still detects all annotations on it and tries to full fill that. I.e. #Autowire other instances.
Either don't use auto wiring or don't mock the class but the interface (which doesn't contain that information).

Resources