Why is #Autowired working differently in application and test? - spring

I made a sample program revealing my question:
package test;
public interface InterfaceA {
}
package test;
public class ClassA implements InterfaceA {
}
package test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class Application {
#Bean
public InterfaceA beanA() {
return new ClassA();
}
#Autowired
private ClassA beanA;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(Application.class);
}
}
#Autowired doesn't work with the concrete class in this application code.
package test;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = Application.class)
public class ApplicationTest {
#Autowired
ClassA beanA;
#Test
public void di() {
System.out.println(beanA);
}
}
But #Autowired works with the concrete class in this test code.
Why is #Autowired working differently in application and test?
I shared the above code at:
https://github.com/izeye/SpringTest

You're using your application context in the main entry point of the application. Make an additional 'Main' class and wire in your context like so:
#Autowired
Application context;
In your test, you're taking the time to start up the application context when you use the annotation: #ContextConfiguration(classes = Application.class).
In your concrete example you're referencing the context in the same class that you're trying to use it in. When you initialize the context in the main, the instance of Application that is running the code has already been instantiated, and did not get autowired, because at the time of its instantiation there wasn't any context for it to use.

It is a bit tricky:
Solution would be to make beanA static method and annotate Application with #Lazy or #DependsOn(value="beanA") annotations. I.e. something like this:
#Configuration
#Lazy
public class Application {
#Bean
public static InterfaceA beanA() {
return new ClassA();
}
#Autowired
private ClassA beanA;
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext(Application.class);
}
}
Reason: for actual beanA bean creation spring need to instantiate Application class first. And while trying to resolve reference to a beanA the only information container has about this future bean is its type (InterfaceA) since there is no actual implementation exists.
However in a case of test class context was already initialized and bean can be resolved by actual implementation class which physically was already created. Same would be if you will try to reference beanA by class from any other spring component.

Related

#Autowired is Null in Spring Boot but same is accessible through Application Context

Through #Autowired i am not able to access the #Component/#Service/#Respository/#Controller class objects in other java files which has #Component annotation (Step 1: Approach) with the Step 1 approach getting Null pointer Exception, but same i could achieve using (Step 2: Approach).
Can anyone please tell me why i am not able to achieve using Step 1 approach:
FYI- I've searched in my entire project i have not used/called/initialized the #Component classes using new method for the autowired class still i getting the issue as "Null Pointer Exception"
Step 1: Using #Autowired Annotation
#Component
public class Processor {
#Autowired
PropertyConfigurator propconfigrator; --> Getting here as null pointer Exception
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}
Step 2: Using ApplicationContext Interface with/without #AutoWired annotation . I am able to get the property value from PropertyConfigurator java file
#Component
public class Processor {
#Autowired
PropertyConfigurator propconfigrator = ApplicationContextHolder.getContext().getBean(PropertyConfigurator.class);
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}
ApplicationContextHolder.java
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
PropertyConfigurator.java file
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
#Service
#Configurable
public class PropertyConfigurator {
private final Properties properties;
public Properties getProperties () {
return properties;
}
public PropertyConfigurator(){
properties = new Properties();
try {
properties.load(getClass().getClassLoader().getResourceAsStream("dbconfig.properties"));
} catch (IOException e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, e.getMessage(), e);
}
}
}
Why did you use #Configurable annotation? In the code you postet, it doesn't make sense. #Configurable is only needed in cases when instances of this class are not createt by spring.
I have changed into Constructor Injection Autowiring as below with the step 1 approach of above (Not using Step 2. It resolved my issue finally.
Not sure why Spring is not able to inject the bean without using the Constructor Autowiring.
Step 1: Using #Autowired Annotation with Constructor
#Component
public class Processor {
#Autowired
public Processor (PropertyConfigurator propconfigrator) {
this.propconfigrator = propconfigrator;
}
public void getDetails(){
System.out.println ("Application URL +propconfigrator.getProperties().getProperty("appURL"));
}
}

Spring Boot Unit Test #Value from .properties File gives NullPointerException

I am trying to read a value from a properties file for a unit test case in Spring Boot. I have two config.properties files, one in src/main/resources:
prop = some-value
and one in src/test/resources:
prop = some-test-value
Main Application class:
package company.division.project;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.context.annotation.PropertySource;
#SpringBootApplication(scanBasePackages = "company.division.project")
#PropertySource(value = "classpath:config.properties")
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
System.setProperty("DUMMY_PROPERTY", "dummy-value");
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
// Do nothing with main
}
}
Service class to be tested:
package company.division.project.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class Service {
#Autowired
Environment environment;
public String getProperty() {
return environment.getProperty("prop");
}
}
ServiceTest class. I have tried two approaches to retrieving the value in the src/test/resources/config.properties file; one with an #Autowired Environment, and one with an #Value annotation...neither worked:
package company.division.project.service;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.test.context.TestPropertySource;
#RunWith(MockitoJUnitRunner.class)
#TestPropertySource("classpath:config.properties")
public class ServiceTest {
#InjectMocks
Service service;
#Autowired
Environment environment;
#Value("${prop}")
private String expectedProperty;
#Test
public void testGetPropertyWithValueAnnotation() {
assertEquals(expectedProperty, service.getProperty());
}
#Test
public void testGetPropertyWithEnvironment() {
assertEquals(environment.getProperty("prop"), service.getProperty());
}
}
I read somewhere on StackOverflow, that in order to auto-wire components in a Spring test class, I'll need to create an entire context for the test, so I tried this (change the annotations and test runner):
package company.division.project.service;
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.env.Environment;
import org.springframework.test.context.junit4.SpringRunner;
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceTest {
#InjectMocks
Service service;
#Autowired
Environment environment;
#Value("${prop}")
private String expectedProperty;
#Test
public void testGetPropertyWithValueAnnotation() {
assertEquals(expectedProperty, service.getProperty());
}
#Test
public void testGetPropertyWithEnvironment() {
assertEquals(environment.getProperty("prop"), service.getProperty());
}
}
The context was created, but both approaches ended in NullPointerExceptions once again.
The problem with your test is that you are trying to use to MockitoJUnitRunner.class in a wrong way.
If you are mocking a Service using #InjectMocks you need to make sure you need to return the value Service.getProperty() by mocking the service call. If you are using SpringRunner.class then you shouldn't have #InjectMocks but should have #Autowired for the service. Following test works.
#RunWith(SpringRunner.class)
#SpringBootTest
public class ServiceTest {
#Autowired
Service service;
#Autowired
Environment environment;
#Value("${prop}")
private String expectedProperty;
#Test
public void testGetPropertyWithValueAnnotation() {
assertEquals(expectedProperty, service.getProperty());
}
#Test
public void testGetPropertyWithEnvironment() {
assertEquals(environment.getProperty("prop"), service.getProperty());
}
}
Thanks to #shazin's answer and some of my own research I've been able to solve the problem.
Basically, there needs to be compatibility between the test runner class specified in #RunWith and the annotations for the Mockito mocks. We want to test the Service class:
Service Class:
#Component
public class Service {
#Autowired
Environment environment;
public String getProperty() {
return environment.getProperty("prop");
}
}
If you're using #RunWith(MockitoJUnitRunner.class), you can use the #InjectMocks and #Mock annotations like below. Whatever is #Autowired in Service will be auto-wired with the mocks:
Test Class with MockitoJUnitRunner:
#RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
#InjectMocks
Service service;
#Mock
Environment mockEnvironment;
#Before
public void before() {
Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
}
}
But you can't auto-wire anything in the test class itself. That requires a Spring Context (a Spring Context is needed to manage the beans which get auto-wired into objects). That's where #RunWith(SpringRunner.class) comes into the picture. You can use it to run a test case with a dedicated Spring context (you'll notice the test case logs showing a new Spring application being booted up for every test class with the #RunWith(SpringRunner.class) annotation). You'll also need to provide the Configuration details with the #SpringBootTest annotation.
The caveat is that a test class with #RunWith(SpringRunner.class) won't understand the #InjectMocks and #Mock annotations; you'll have to use the #MockBean annotation. This will effectively modify the Spring context by replacing beans with their mocks; anything with the #Autowired annotation will get auto-wired with the mock beans automatically:
Test Class with SpringRunner:
#RunWith(SpringRunner.class)
#SpringBootTest(classes=Application.class)
public class ServiceTest {
#Autowired
Service service;
#MockBean
Environment mockEnvironment;
#Before
public void before() {
Mockito.when(mockEnvironment.getProperty("prop")).thenReturn("some-test-value")
}
}
So...using the #RunWith(SpringRunner.class) didn't achieve anything except change the names of the annotations (#InjectMocks -> #Autowired, and #Mock -> #MockBean), right? Wrong. Using SpringRunner gives you the power of auto-wiring components within your test case. So if you want to use an actual Environment (not a mock one), you can do that as well; just auto-wire it in from the dedicated Spring context:
Test Class with SpringRunner and #Autowired Environment:
#RunWith(SpringRunner.class)
#SpringBootTest(classes=Application.class)
public class ServiceTest {
#Autowired
Service service;
#Autowired
Environment environment;
#Test
public void testServiceGetProperty() {
assertEquals(environment.getProperty("prop"), service.getProperty("prop");
}
}
And that solves the problem.

Cannot inject property value to class parameter (#Value annotatnion)

I'm learing Spring Boot and I have probably very simple question, but it's not clear enough for me. I'm facing some problem with #Value annotation - I would like to know why apprication property cannot be injected to class parameter.
I prepared some very basic project using Spring Initializr and I added one property to my "application.properties" resource. Moreover, I created two additional classes: "YellowCar" (which works fine) and "RedCar" (which does not work - the parameter cannot be properly injected).
"application.properties" file:
car.age=15
The main class of my application:
package com.example.helper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.core.env.Environment;
#SpringBootApplication
public class HelperApplication implements CommandLineRunner {
#Autowired
private Environment env;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println (new RedCar(env));
System.out.println (new YellowCar());
}
}
RedCar is build by passing the Environment variable to constructor:
package com.example.helper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class RedCar {
private int age;
#Autowired
public RedCar (Environment env) {
this.age = new Integer(env.getRequiredProperty("car.age")).intValue();
}
#Override
public String toString() {
return "Car [age=" + age + "]";
}
}
YellowCar is build without passing the Environment variable to the constructor, but using the #Value annotation:
package com.example.helper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class YellowCar {
#Value("${car.age}")
private int age;
#Override
public String toString() {
return "YellowCar [age=" + age + "]";
}
}
This is the program output:
Car [age=15]
YellowCar [age=0]
As you can see, age of YellowCar was not properly injected (it equals 0).
My goal: I'd like not to pass everywhere Environment object to the constructor of other classes... I'd like to use #Value annotatnio instead. Could somebody explain me:
1) why my code is not working?
2) how this code should be updated in order to get the following output?
Car [age=15]
YellowCar [age=15]
Thanks!
This happens, because you instantiate YellowCar directly. In order to make thing work, #Autowire YellowCar inside HelperApplication and use this injected instance like this(not tested):
#Autowired
private YellowCar yellowCar;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println (new RedCar(env));
System.out.println (yellowCar);
}
Explanation:
When you creating class using new, Spring knows nothing about this new instance, hence, it cannot inject anything. But when instance is created through Spring infrastructure (I'm trying not use a lot of slang words here), it will have all fields injected: when you mark class with #Component annotation or create method with #Bean annotation, you tell Spring, that you want it to be a Bean. On application startup, spring creates instances of this beans - by default only one instance per context - and inject this instance into all other beans, where requested. In this case, this instance is being processed by spring infrastructure (exact class which do it - AutowiredAnnotationBeanPostProcessor), and instances of other beans will be injected into our bean.
UPD: You can do the same thing with RedCar:
#Autowired
private YellowCar yellowCar;
#Autowired
private RedCar redCar;
public static void main(String[] args) {
SpringApplication.run(HelperApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
System.out.println (redCar);
System.out.println (yellowCar);
}

Failing while testing autowired field using Junit

Am new for Junit, any solution for below issue is welcomed.
I have a main class like,
#Service
public class MainClass extends AbstractClass {
#Autowired
ClassA a;
#Autowired
ObjectMapper mapper;
public void methodA(){
....
AnotherClass obj= (AnotherClass)mapper.readerFor(AnotherClass.class).readValue(SOME_CODE);
.......
}
Test Class is,
#RunWith(PowerMockRunner.class)
#PrepareForTest({MainClass.class})
public class MainClassTest {
#Mock
ClassA a;
#Mock
ObjectMapper mapper;
#InjectMocks
MainClass process = new MainClass();
//I have to do somthing for Autowired mapper class of main in test class as well
#Test
public void testProcessRequest() throws Exception{
process.methodA()
}
Am getting null for mapper object in main class while testing, Yes am aware that I haven't dne any kind of initialization.
Is there a better way for writing the junit mapper.
Note : I tried #Mock for ObjectMapper which throws exception at "readerFor".
Thanks in advance.
You do not have to use Mockito/powerMock. Just use a spring boot test.
Something like this:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import com.fasterxml.jackson.databind.ObjectMapper;
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeServiceTest {
#Autowired
private SomeService service;
#Autowired
private ObjectMapper om;
#Test
public void try_Me(){
System.out.println(om);
}
}
Adding a some info more to your question.
If you really want to use mockito for the ObjectMapper you should prepare the mock. If not when calling readerFor(...) the mock returns null by default and later, in the readValue method, you are getting a nullpointer.
A basic preparation for the mock might be:
ObjectReader or = Mockito.mock(ObjectReader.class);
Mockito.when(or.readValue(Mockito.anyString())).thenReturn(new instance of your object);
Mockito.when(mapper.readerFor(User.class)).thenReturn(or);

Spring Boot Annotation #Autowired of Service fails

I'm trying to use #Autowired annotation for a Service class in Spring Boot application, but it keeps throwing No qualifying bean of type exception. However, if I change the service class to a bean, then it works fine. This is my code:
package com.mypkg.domain;
#Service
public class GlobalPropertiesLoader {
#Autowired
private SampleService sampleService;
}
package com.mypkg.service;
#Service
public class SampleService{
}
And this is my SpringBoot class:
package com.mypkg;
import java.util.Set;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
#SpringBootApplication
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableTransactionManagement
public class TrackingService {
private static final Logger LOGGER = LoggerFactory.getLogger(TrackingService.class);
static AnnotationConfigApplicationContext context;
public static void main(String[] args) throws Exception {
SpringApplication.run(TrackingService.class, args);
context = new AnnotationConfigApplicationContext();
context.refresh();
context.close();
}
}
When I try to run this, I get the following exception:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mypkg.service.SampleService] 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)}
But when I remove the #Service annotation from the SampleService class, and add it as a bean in my AppConfig class as below, it works fine:
#Configuration
public class AppServiceConfig {
public AppServiceConfig() {
}
#Bean(name="sampleService")
public SampleService sampleService(){
return new SampleService();
}
}
The classes are in different packages. I am not using #ComponentScan. Instead, I'm using #SpringBootApplication which does that automatically. However, I tried with ComponentScan as well but that didn't help.
What am I doing wrong here?
You are using two ways to build a Spring's bean. You just need to use one of them.
#Service over the POJO
#Service
public class SampleService
#Bean in the configuration class which must be annotated with #Configuration
#Bean
public SampleService sampleService(){
return new SampleService();
}
#Autowired is resolved by class type then #Bean(name="sampleService") is not needed is you have only one bean with that class type.
EDIT 01
package com.example
#SpringBootApplication
public class Application implements CommandLineRunner {
public static void main(String... args) {
SpringApplication.run(Application.class);
}
#Autowired
private UserRepository userRepository;
#Autowired
private UserService userService;
#Override
public void run(String... strings) throws Exception {
System.out.println("repo " + userRepository);
System.out.println("serv " + userService);
}
}
package com.example.config
#Configuration
public class AppConfig {
#Bean
public UserRepository userRepository() {
System.out.println("repo from bean");
return new UserRepository();
}
#Bean
public UserService userService() {
System.out.println("ser from bean");
return new UserService();
}
}
package com.example.repository
#Service
public class UserRepository {
#PostConstruct
public void init() {
System.out.println("repo from #service");
}
}
package com.example.service
#Service
public class UserService {
#PostConstruct
public void init() {
System.out.println("service from #service");
}
}
Using this code you can comment the AppConfig class and then you will see how UserRepository and UserService are autowired. After that comment #Service in each class and un-comment AppConfig and classes will be autowired too.

Resources