Different behaviour of injected fields in unit test and deployed version - spring

I am new in lombok and spring and I have one issue which I am trying to understand. Firstly my code was design as below and then I modified it because I needed to have a setter.After that all tests passed. But after deployment I started to get null pointer exception for the service field.
So my question is why I am getting null pointer exception? I think after removing final keyword lombok does not create constructor for that field. But still I do not understand why it did not fail on unit tests if that is the reason? And what is the solution for it ?
This is the first version which works.
#Component
#RequiredArgsConstructor
public class MyClass{
private final MyServiceservice service;
private final MyOtherService otherservice;
}
This the version after update and where service field is null.
#Component
#RequiredArgsConstructor
public class MyClass{
#Setter
private MyServiceservice service;
private final MyOtherService otherservice;
}

Related

How to test a spring component with multiple dependencies?

Trying to write some junits for a component. The issue im having is that that component has an autowired dependency, which itself has 3 autowired dependencies. So when I try to test a method, I keep getting a npe.
#Component
public class Transformer {
private CacheService cacheService;
public Transformer(CacheService cacheService) {
this.cacheService = cacheService;
}
public void doAction(CustomObject o){
cacheService.perform(o);
}
#Component
public class CacheService {
#Autowired private GenericBean genericBean;
#Autowired private Dao dao
public void doAction(CustomObject o){
dao.fetch(o);
}
}
Once it gets to that doAction method i get a npe since all those autowired beans are null. How do I get past this issue? I've tried a few past solutions I saw here, but none worked.
Consider using constructor level injection instead of field level injection (#Autowired on fields). Then you can construct your CacheService with needed mocks. this is best practice in most cases.
Otherwise check Spring #InjectMocks annotation.

#AllArgsConstructor and Constructor Injection with Spring: is private final needed?

Let's say I have the following constructor injection (not Autowiring):
#Service
public class FooService {
private final OrderService orderService;
public FooService(OrderService orderService) {
this.orderService = orderService;
}
}
That can be replaced with:
#Service
#AllArgsConstructor
public class FooService {
private final OrderService orderService;
}
Do I need to declare this as private and final to inject this service? Does Lombok take care of this like they do with #Data and beans? Any side-effects?
You should use #RequiredArgsConstructor instead, you need a single primary constructor to fill the required fields. So you marked them final and use this annotation to generate a primary constructor.
#AllArgsConstructor is bug-prone, because it may produce multiple constructors which Spring may fail to handle.
In your particular case the results of #AllArgsConstructor and #RequiredArgsConstructor just happen to be the same, because you have just one final field.
Note that, Spring documentation encourages the usage of constructor-based injection (https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#beans-setter-injection), and recommends to avoid using several injection techniques together.
#Service
#RequiredArgsConstructor
public class FooService {
private final OrderService orderService;
}
According to documentation
#AllArgsConstructor generates a constructor with 1 parameter for each field in your class. Fields marked with #NonNull result in null checks on those parameters.
So, no, it does not make your fields private & final as for example #Value annotation.

Advice not applied in unit tests

Spring AOP advices are not being applied for unit tests. Everything seems to be working fine during normal execution and integration tests, but are not applied while running unit tests.
Relatively new to Spring and battling with this issue for a while. Looks like some configuration issue. Tried with different types of runners but did not have any luck. Also tried to integrate with AspectJWeaver for compile time weaving but hit many compile issues across the legacy code base which I stepped back.
Unit test
#RunWith(SpringRunner.class)
#SpringBootTest
#EnableAspectJAutoProxy
public class UserServiceImpl
private UserServiceImpl userServiceSpy;
#Mock
private UserDao userDao;
#Mock
private MembershipDao membershipDao;
#Mock
private Service1 service1;
#Mock
private Service2 service2;
#Mock
private TroubleshootingLogService troubleshootingLogService;
#Before
public void setup() {
UserServiceImpl userService = new UserServiceImpl(userDao, membershipDao,service1, service2, <param1>, <param2>);
userServiceSpy = spy(userService)
// some other variables inits...
}
// All the unit tests.
Integration test
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#TestExecutionListeners(value = {FlywayTestExecutionListener.class}, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
#FlywayTest
#ActiveProfiles("local")
public class UserServiceIntegrationTest {
#ClassRule
public static final WireMockClassRule wireMockRule = new WireMockClassRule(wireMockConfig().dynamicPort());
#Autowire
private UserDao userDao;
#Autowire
private MembershipDao membershipDao;
#Autowire
private Service1 service1;
#Autowire
private Service2 service2;
#Before
public void init(){
//clean up persisted test states
}
// All integration tests
}
Aspect
#Aspect
#Component
#Order(1)
public class UserExceptionLoggingAdvisor extends AbstractExceptionLoggingAdvisor {
private static final Logger LOGGER = LoggerFactory.getLogger(UserExceptionLoggingAdvisor.class);
#Around("#annotation(LogException) && args(directoryId, userId, userToUpdate)")
public Object handleException(ProceedingJoinPoint joinPoint, String directoryId, String userId, ExternalUser userToUpdate) throws Throwable {
LOGGER.debug("Advising execution to handle possible ScimException");
}
When we have a breakpoint on the Aspect class, at private static final Logger LOGGER = LoggerFactory.getLogger(UserExceptionLoggingAdvisor.class); line, the unit test breaks. But it does not break at the actual #Around advice for unit tests while it does for integration tests.
Can anyone advise me on how to fix this issue.
It's not a "configuration issue"
Since you havn't disclosed how you are calling your unit tests and none of the classes. My guess is that it isn't triggered since you have expressed the fully qualified name to your LogException annotation.
it should be my.full.packagename.logexception
My advice is to keep your "unittests" as simple as possible and give up that idea of getting Aspects to work in your unit test.
Too many mocks is never a good thing,
Testing on the Toilet: Don’t Overuse Mocks

Trying to generate error about Spring's #Autowired field injection for JUnit

I am working with Spring 4.0.7, and with JUnit about testing of course.
About DI Spring offers #Autowired to be used in three locations
constructor
setter
field
I always work through the two first, why never the third option?
Because I remember have read long time ago about field injection should not be used, because it has a negative consequence about Testing. It does JUnit fails.
Note: Only for Testing is the problem. To runtime or production all goes well
Objective: For demonstration/academic purposes I want generate this problem.
I have the following:
Repository
public interface PersonRepository extends JpaRepository<Person, String>{
}
A Service
#Service
#Transactional
#Profile("failure")
public class PersonFailTestServiceImpl implements PersonService {
private static final Logger logger = ...
#Autowired
private PersonRepository personRepository;
Other Service (calling or using the service shown above)
#Service
#Transactional
#Profile("failure")
public class PersonFailTestProcessImpl implements PersonProcess {
private static final Logger logger = ...
#Autowired
private PersonService personService;
How you can see the two services are based on Field Injection.
Now the testing:
How the beans are loaded
#Configuration
#ComponentScan( basePackages={"com.manuel.jordan.server.infrastructure"},
basePackageClasses={PersonProcess.class,PersonRepository.class, PersonService.class})
public class CentralConfigurationEntryPoint {
}
#ContextConfiguration(classes=CentralConfigurationEntryPoint.class)
public class CentralTestConfigurationEntryPoint {
}
Now the two testing classes
#Transactional
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles({"development","failure"})
public class PersonServiceImplDevelopmentFailureTest extends CentralTestConfigurationEntryPoint {
#Autowired
private PersonService personService;
#Test
public void savePerson01(){
Person person01 = PersonFactory.createPerson01();
personService.save(person01);
personService.printPerson(personService.findOne("1"));
}
#Transactional
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles({"development","failure"})
public class PersonProcessImplDevelopmentFailureTest extends CentralTestConfigurationEntryPoint{
#Autowired
private PersonProcess personProcess;
Well all the testing methods pass, all green. I don't know if I am missing something or through Spring 4 the problem has been fixed
If this was your premise or problem
Because I remember have read long time ago about field injection
should not be used, because it has a negative consequence about
Testing. It does JUnit fails.
then you thought wrong. There is nothing inherently wrong with using field injection, definitely nothing that would cause JUnit tests to fail in and of itself. If a bean exists, Spring will be able to inject it whether it's in a constructor, a setter method, or a field.
Since you've activated your failure profile, your PersonFailTestServiceImpl bean will be found.
I think I can help. The example code you've posted here is a good example of a system / integration test, not a UNIT test.
If you were UNIT testing PersonFailTestProcessImpl, you would have to set the personRepository dependency yourself through code. But it is private, so how do you do this? You cannot use a constructor or setter since none is provided. This is what is meant by 'hard to unit test'.
Java 5+ provides a way to set private variables like this via reflection (the so-called privileged accessor). Basically, you obtain the class, get the declared field, call its setAccessible method, then you can set its value directly. There are libraries that will do these steps for you, but the point is that this is a pain compared to X.setSomething();
So there is nothing that 'makes jUnit fails' by using #Autowired on a private field. But building an object model without constructors or setters for establishing dependencies is unnecessarily constraining.

#Autowired in Spring MVC #Controller does not work on a private field

I have a Spring MVC Controller in a very XML-slimmed application, we use a lot of annotations and as little config as possible. The Controller is working and it also has a number of resource values injected. But I've experienced a really strange behavior with this controller; annotated private fields referencing other components will not be injected.
This will not work.
#Controller
public class EntranceUnitController {
#Value("${remote.baseUrl}")
private String baseUrl = "http://localhost";
#Value("${remote.port}")
private String pushPort = "8080";
#Autowired
private HttpClientFactory httpClientFactory;
...
It seems that the httpClientFactory isn't there yet when the private fields are set, if I set a break point to inspect the value there is of course null set when the controller is created.
BUT, if I make a setter for the component and annotate the set-method instead of the private field the controller works as expected.
#Controller
public class EntranceUnitController {
#Value("${remote.baseUrl}")
private String baseUrl = "http://localhost";
#Value("${remote.port}")
private String pushPort = "8080";
private HttpClientFactory httpClientFactory;
#Autowired
public void setHttpClientFactory(HttpClientFactory httpClientFactory) {
this.httpClientFactory = httpClientFactory;
}
...
To me this is really annoying. Isn't the auto wiring injection for annotated values happening at the same time regardless injection point? I.e. why does it matter that the object is injected with a setter? I thought that private field injections are directly followed by constructs and setters, me start to think I'm wrong in that case...
Seems like your dependencies are in fact injected, you are just putting a breakpoint in the wrong moment (too early) and the dependencies aren't injected yet, despite class being already created.
Remember that, unless you are using constructor injection, the first place where you can use injected dependencies is #PostConstruct method:
#Controller
public class EntranceUnitController {
#Autowired
private HttpClientFactory httpClientFactory;
#PostConstruct
public void init() {
httpClientFactory //should not be null
}

Resources