Spring Test with dependency Injection of real objects and mocks - java

I have a test use case in which based on a boolean property we decide which one of 2 Spring Beans to create upon application startup.
For simplicity, let's call them ProfileServiceA and ProfileServiceB.
When the boolean property is set to false (the default) it creates ProfileServiceA and when it's true it creates ProfileServiceB.
This is done inside of a configuration class annotated with #Configuration.
My test is pretty simple, I want to verify which one of the Beans is loaded in the Spring Context when our application is starting based on that property.
For each of those Beans, when we create them, the constructor gets multiple objects in order to be instantiated.
Most of the objects I have annotated with the #MockBean because they are only being pointed to in the constructor like we usually do with Dependency Injection and that's not a problem for me.
One of those properties is not only pointed at in the constructor but is actually being used to build one of the fields.
in the constructor of ProfileService we do something like this:
this.url = securityProperties.getProfile().getProtocol() + "://" + securityProperties.getProfile().getHost() + ":" + securityProperties.getProfile().getPort() + "/" + securityProperties.getProfile().getEndpoint();
The SecurityProperties class has the inner static class Profile, So when the application context starts to load and this Bean is being instantiated in the constructor (even before the method annotated with #Before) I fail on NullPointerException because the object Profile is still null (The SecurityProperties is not because it's annotated with #MockBean).
The SecurityProperties and its inner static class Profile are not defined as Beans but still, I tried to set the Profile object inside of the test class to be also #MockBean or #Mock but this didn't solve the issue.
I figured that I probably need to inject "real" Objects into the context but I couldn't find a wiki/docs on how to inject "real" objects WITH Mocked ones to an Autowired Spring Bean.
The SecurityProperties is not a Spring Bean but a regular data object (POJO).

You can implement the BeanPostProcessor interface in your test code. In the implementation of postProcessBeforeInitialization, if the current bean name is securityProperties (or whatever name your SecurityProperties bean has) then mock the behavior of SecurityProperties::getProfile() to return an actual Profile instance to avoid the NPE.
This works because ProfileService depends on SecurityProperties so the latter will be initiated first and right after that your postProcessBeforeInitialization will be called, right before the instantiation of ProfileService

Related

How to Lazy load all the Spring beans whether it is defined by #Bean or #Component in Springboot 2.2

I am writing a spring application which is interactive and basically handles lots of commands like create, list, update, delete various types of resources.
For now, just assume a single run of the application handles only a single command and the program exits.
For all the classes to validate command, execute the command, required factory classes for each resource there is a separate class and each class is annotated with #Component annotation for spring to manage all the components.
There are also some of the manually defined beans by #Bean method.
Now that my application first identifies what kind of command is executed (create, delete, list, update, etc), I want the only beans of that command to be created and Autowired wherever required (after taking command from user) and I want to avoid the creation of dozens of beans related to other commands.
On searching, I came to know about Lazy instantiation of Beans that spring provides.
However, I am not sure if it is the weapon I am searching for.
What I tried
Very first I found #Lazy annotation, but since I want to lazily load all the Beans, I don't want to write #Lazy everywhere in each class.
Then I found setting below property in application.yml does the work.
spring:
main:
lazy-initialization: true
I tried that but still, it is not lazily creating the beans.
My application.yml files looks like this
spring:
main:
lazy-initialization: true
My main SpringBootApplication file looks like this:
#Slf4j
#SpringBootApplication
public class SpringBootApplication {
public static void main(String[] args) {
System.out.println("Loading Application...");
ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);
final AtomicInteger counter = new AtomicInteger(0);
log.info("**************** START: Total Bean Objects: {} ******************", context.getBeanDefinitionCount());
Arrays.asList(context.getBeanDefinitionNames())
.forEach(beanName -> {
log.info("{}) Bean Name: {} ", counter.incrementAndGet(), beanName);
});
log.info("**************** END: Total Bean: {} ******************", context.getBeanDefinitionCount());
}
}
My other classes looks like this:
#Component
#RequiredArgsConstructor(onConstructor = #__(#Autowired))
public class MyClass1 implements ResourceCreator<MyClass2, MyClass3> {
private final RequestValidatorImpl requestValidator;
private final ResourceCreator resourceCreator;
#Override
public MyClass2 toImplementFunction(MyClass3 myclass3) {
//logic
}
On running the application, It prints all the classes where I annotated #Component as well as beans created by #Bean method.
I have also tried using below in Application.properties but still no use.
spring.main.lazy-initialization=true
Also, if you wish, please comment on whether I should use #Component for each Class like I am using or not and what is better practice instead.
I think you misunderstood the meaning of the lazy flag you are passing,
It means that the object will be created only when it is invoked but it does not say that it will not scan that package. Spring will scan all packages and store bean definition names but it will create the objects only when it is invoked, if you have passed the lazy flag to it.
You can verify this behaviour by checking the number of beans created when you pass the lazy flag as true and false.
you can check it as given below
ApplicationContext context = SpringApplication.run(SpringBootApplication.class, args);
System.out.println("count:"+context.getBeanDefinitionCount());
Edit on Apr/7th 2020 start
Another way to do that is create a constructor and use that to inject the autowired properties and print out a log when they enter the constructor.
I did the same in a sample project and below is he result, first one is for eager initialization and next one for lazy.
spring.main.lazy-initialization=false
Application logs
Inside Constructor
calling bean
inside bean method
spring.main.lazy-initialization=true
Application logs
calling bean
Inside Constructor
inside bean method
Edit on Apr/7th 2020 end
Please mark this as answered if I answered your question.
Thank you
Long story short:
For anyone wanting to lazily initialize their whole Spring Boot context, setting this property to true is the way to go:
spring.main.lazy-initialization=true
Pro tip:
It can be used in combination with the #Lazy annotation, set to false; so all the defined beans will use lazy initialization, except for those that we explicitly configure with #Lazy(false).
In such a way, the lazy initialization becomes opt-out instead of the default opt-in.

Constructor bean injection with custom annotation

I would like to inject a bean (lets call it clientStub) into my service bean.
There are two requirements for that:
In order to create the clientStub bean, I need to access the #Client annotation, that carries some important information that are used to lookup the relevant configuration in the properties.
It must support constructor based injection (and #Bean method parameter injection)
Expected usage:
#Autowired
public MyService(
#Client("invoice-manager") InvoiceManagerClientStub clientStub,
SomeOtherBean bean1, ...) {
or
#Bean
MyService myService(
#Client("invoice-manager") InvoiceManagerClientStub clientStub,
SomeOtherBean bean1, ...) {
So I would like to do the same as the #Value annotation e.g. derive the value from the annotation on the parameter (plus some internal lookups).
Unfortunately, the usual BeanFactorys don't seem to be aware of the target's annotations.
Alternatives considered
Using an BeanPostProcessor to inject the values into annotated fields. Well, this is what I'm currently doing, but it doesn't feel right to have an immutable class with a single setter for injection.
Injecting it to a field and then exposing it as a bean or manually invoking the constructor is even worse.
Creating a proxy instance of the injected class. This isn't possible since the injected classes are generated + final and not part of my library.
Derive the configuration from the bean name: Not sure how to implement this and how to explain the user what the configuration parameters should be named in their properties files. I also would like to avoid bean name conflicts.
Non-Goals
Overwriting any core beans. I would like to distribute the extension as a library (spring-boot based), so any excessive replacement of spring internal beans should be avoided.
TLDR
How do I tell spring to resolve the parameter using my annotation's value (resolver)?

get a bean instance inside a non managed spring class if I am retrieving it through ApplicationContext getBean method

I know I can not use #Autowired annotation in a non managed spring class. But I notice I do able to get a bean instance inside a non managed spring class if I am retrieving it through ApplicationContext getBean method.
can someone explain me why is that ? what is the different between the options ? in both spring doesn't know the non managed class
1) You can use #Autowired in a non-managed spring class if you use #Configurable -with the usage of internal aspect weaving spring will manage to autowired the referenced beans into your "Configurable" class when it is constructed with the "new" operator. Here is an article how this works: http://www.javacodegeeks.com/2013/09/spring-configurable-magic.html
If a class is not configurable spring cannot notice when a new instance is created to autowire its references.
2) The ApplicationContext is "Central interface to provide configuration for an application." Here you have access to the whole spring managed beans etc. That's why you can get everything due to accessing it via ApplicationContext. http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/ApplicationContext.html
ok, so here's the major points:
the "bean declaration" (either in xml or java) is just a recipe of how to instantiate the object (not a object itself).
when spring application boots, the beanFactory receives this recipes from beanDefinitionReader, instantiates objects according to them (recipes) and then pass them (objects) to a list of beanPostProcessors (several times) that are "injecting" dependences to the instantiated objects and then puts the objects into hashMap.
roughly saying applicationContext is a class exposing access to this beans;
and that's how you can access this beans out of spring application using applicationContext.
another thing is that, actually you can inject beans into non managed beans through #Configurable. in this case the AspectJ would be used for making this work

spring boot + junit + overriden component instance

I've got sample project here. I'd like to setup som junit test with class com.github.bilak.axonframework.poc.command.user.UserTest
When I run test, I can see in log
Skipping bean definition for [BeanMethod:name=userCommandHandler,declaringClass=com.github.bilak.axonframework.poc.command.config.CommandConfiguration]: a definition for bean 'userCommandHandler' already exists. This top-level bean definition is considered as an override.
And then I can see that when UserRepository is injected to UserCommandHandler that's another instance as that which is used in UserTest class. Why this is done and how can I avoid this?
Thanks
It's because a bean of name userCommandHandler is defined twice in context. First as an annotated component com.github.bilak.axonframework.poc.command.user.UserCommandHandler registered with component scan, second as a bean defined in CommandConfiguration (line 75).
And then I can see that when UserRepository is injected to UserCommandHandler that's another instance as that which is used in UserTest class.
Looking at the GivenWhenThenTestFixture implementation I can see that it actually wraps the passed instance into some adapter. Moreover, because what you passes is actually a spring proxy which has the userRepository direct reference null (that's correct I believe), the effective registered handler has it also null, so you are probably experiencing null pointer exception because of that. If I change the setUp for your test:
#Before
public void setup() throws Exception {
fixture = Fixtures.newGivenWhenThenFixture(User.class);
UserCommandHandler target = (UserCommandHandler) ((Advised) userCommandHandler).getTargetSource().getTarget();
fixture.registerAnnotatedCommandHandler(target);
}
It helps.

#Value place holder not resolving values in certain spring beans

I have a situation and im trying to figure out why it is so. The scenario is I have a class A that implements an operation exposed by a webservice. For each request for the operation a new instance of A is created. The entire thing is manged by Spring internally and I am not sure how exactly the Class A in instantiated by the webservice.
Now i have a class variable whose value i want to inject during runtime through
#Value("${someValue}")
String someVariable
someValue is defined a system property. But each of the beans created has someVariable as NULL.
I tried the same thing in another class annotated with #Configuration in the same package and it is resolving the variable fine.
So, Im thinking if the ability to inject through #Value depends on the way a bean is created like #Component, #Configuration-#Bean, XML definiti
Not really. Actually You can inject value with #Value in both the component scan bean and xml definition bean. But pay attention to this, It depends on which xml file that you put the Placeholder in, it just valids in the per-container. For example, you put it in applicationContext, then the bean create in xxx-servlet.xml can not inject value with #Value.

Categories