Null pointer on an autowired bean which is not mocked by mockito - java

I have a service class that I need to unit test. The service has a upload method which in turn calls other services(autowired beans) that updates the database. I need to mock some of these services and some to execute as it is.
#Service
public class UploadServiceImpl implements UploadService{
#Autowired
private ServiceA serviceA;
#Autowired
private ServiceB serviceB;
public void upload(){
serviceA.execute();
serviceB.execute():
//code...
}
In the above example I need to mock ServiceA, but i would like ServiceB to run as is and perform it's function.
My Junit test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes=Swagger2SpringBoot.class)
public class UploadServiceTest {
#Mock
private ServiceA serviceA;
#InjectMocks
private UploadServiceImpl uploadService;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
}
#Test
public void testUpload(){
uploadService.upload();
}
When I execute this I get NPE at serviceB.execute(); in UploadServiceImpl.
What could be the problem?
Note: I am not specifying the behavior of the mocked object because I don't really care and also default behavior of mocked objects are to do nothing.
Thanks!

Usually when unit testing you want to mock all external dependencies of a class. That way the unit test can remain independent and focused on the class under test.
Nevertheless, if you want to mix Spring autowiring with Mockito mocks, an easy solution is to annotate with both #InjectMocks and #Autowired:
#InjectMocks
#Autowired
private UploadServiceImpl uploadService;
The net effect of this is that first Spring will autowire the bean, then Mockito will immediately overwrite the mocked dependencies with the available mocks.

The issue you are facing is due to the use of #InjectMocks annotation.#InjectMocks marks a field on which injection should be performed. Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection – in this order. If any of the given injection strategy fail, then Mockito won’t report failure.
So in your case when trying to inject mocks only one mock bean is present and the other bean ServiceA is not getting injected.To solve this issue :
You can try not using #InjectMocks at all instead pass a mock object for the method that you want to mock while pass rest of the autowired objects into the constructor.Example :
Here to test i am passing one mock object and one autowired object.
#RunWith(MockitoJUnitRunner.class)
public class SampleTestServiceImplTest {
#Mock
private SampleClient sampleClient;
#Autowired
private BackendService backendService ;
private BackendServiceImpl backendServiceimpl;
#Before
void setUp() {
backendServiceimpl = new BackendServiceImpl(sampleClient, backendService);
}
Or another way you can make this work is by using #Autowired annotation along with the #InjectMocks.#Autowired #InjectMocks are used together and what it will do is inject the mocked class and Autowired annotation adds any other dependency which the class might have.
Answer referred from : https://medium.com/#vatsalsinghal/autowired-and-injectmocks-in-tandem-a424517fdd29

In my opinion, we are writing unit test cases and we should not initialize the spring context in order to test a piece of code.
So,
I used Mockito to mock the Autowired beans in my main target test class and injected those mock beans in my main test class Object
maybe sounds confusing, see the following example 💥
Dependencies I used
testImplementation("org.mockito:mockito-core:2.28.2")
testImplementation("org.mockito:mockito-inline:2.13.0")
testImplementation("org.junit.jupiter:junit-jupiter:5.8.2")
testImplementation("org.mockito:mockito-junit-jupiter:4.0.0")
My main class is Maths and Calculator bean is autowired
class Maths{
#Autowired Calculator cal;
.........
.........
public void randomAddMethod(){
cal.addTwoNumbers(1,2); // will return 3;
}
}
Test class
#ExtendWith(MockitoExtension.class)
class MathsTest{
#Mock(answer = Answers.RETURNS_DEEP_STUBS) Calculator cal;
#InjectMocks Maths maths = new Maths();
#Test testMethodToCheckCalObjectIsNotNull(){
maths.randomAddMethod();
}
}
Now cal will not be null in Maths class and will work as expected

Add
#Mock
private ServiceB serviceB;
to create injectable mock of missing service just like you did with service A.

In my case, beside using combination of #InjectMocks and #Autowired, I also had to provide setter for the mocked object in the tested class (setter for ServiceA in UploadServiceImpl in the original example). Without that the real method of ServiceA was called.

Another way is to define an autowired constructor so that you can test the services properly.
#Service
public class UploadServiceImpl implements UploadService{
private ServiceA serviceA;
private ServiceB serviceB;
#Autowired
public UploadServiceImpl(ServiceA serviceA, ServiceB serviceB) {
this.serviceA = serviceA;
this.serviceB = serviceB;
}
public void upload(){
serviceA.execute();
serviceB.execute():
//code...
}

I couldn't make it work without using ReflectionTestUtils. Setting the constructor is one option if it's viable for you.

Related

#InjectMocks inject #MockBean by Constructor and setter not working properly

I have try so many time by unsing #RunWith(SpringJUnit4ClassRunner.class)
I have tried to create a test case foe a class with getter and Constructor injection. When i user #MockBean for setter injection, #Mock for Constructor injection and also use #RunWith(SpringJUnit4ClassRunner.class) and MockitoAnnotations.initMocks(this); bean injection.
If i comment MockitoAnnotations.initMocks(this); constructor injection not working.
Now all beans are injected perfectly but #Mock beans(Contructor injected ) beans mocked mthods not working properly when its called.
#Component
Class A{
}
#Component
Class B {
}
#Component
Class c{
}
#Component
Class D{
#Atowired
A a;
B b;
C c;
#Autowired
public D(B b,C c){
b=b;
c=c;
}
}
My Test Class is
#RunWith(SpringJUnit4ClassRunner.class)
Class TestClass{
#MockBean
A mockA
#Mock
B mockB
#Mock
C mockC
#InjectMocks
D mockD
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);//Without this Constructor injection not working
when(mockA.getValue()).then("StringValA");
when(mockB.getValue()).then("StringValB");
when(mockC.getValue()).then("StringValC");
}
#Test
public void testMethod(){
mock.getAllValues();// It will call all injested bean method we are mocked in #before
}
}
The injections are working properly ,issue is belongs to mocked methods of beans which i use #Mock is not working properly means mockB.getValue() and mockC.getValue() retun null but mockA.getValue() return correctly when i test run.
If you are running a test with SpringJUnit4ClassRunner.class then you need to use #MockBean instead of #Mock.
Please refer to the spring boot documentation
Also, you need to use #Autowired instead of #InjectMocks.
#RunWith(SpringJUnit4ClassRunner.class)
Class TestClass{
#MockBean
A mockA
#MockBean
B mockB
#MockBean
C mockC
#Autowired
D mockD
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);//Without this Constructor injection not working
when(mockA.getValue()).then("StringValA");
when(mockB.getValue()).then("StringValB");
when(mockC.getValue()).then("StringValC");
}
#Test
public void testMethod(){
mock.getAllValues();// It will call all injested bean method we are mocked in #before
}
}
When you run the test with spring runner, you must specify what exactly would you like to load as beans (read, let spring know what exactly should be included into the application context).
Usually this can be done with #ContextConfiguration annotation.
I suspect that since you don't specify this annotation, spring doesn't really loads any of your components (A, B, C in the question, etc).
Now #MockBean basically allows "altering" the application context for test purposes. It does so by providing a mock instead of a real bean that should have been loaded in "regular" application context.
In this case, there is no point to call MockitoAnnotations.initMocks(this); Spring will inject the mocks by itself once everything is configured properly.
Solution :
#RunWith(SpringJUnit4ClassRunner.class)
Class TestClass{
#MockBean
A mockA
#MockBean
B mockB
#MockBean
C mockC
#Autowired
D mockD
#Before
public void setUp() {
mockD = new D(mockA,mockB);
MockitoAnnotations.initMocks(this);
when(mockA.getValue()).then("StringValA");
when(mockB.getValue()).then("StringValB");
when(mockC.getValue()).then("StringValC");
}
#Test
public void testMethod(){
mock.getAllValues();// It will call all injested bean method we are mocked in #before
}
}
Normal bean initialize methods like SpringJUnit4ClassRunner or MockitoAnnotations.initMocks(this); handling only one type injection at a time. Either constructor or Auto wired injection.
#RunWith(SpringJUnit4ClassRunner.class)
means public class SpringJUnit4ClassRunner extends BlockJUnit4ClassRunner. SpringJUnit4ClassRunner is a custom extension of JUnit's. It will initialize mock the #MockeBean and #bean anotted beans at the intial time of test run.Alsoi runnig the bean injection also.
MockitoAnnotations.initMocks(this) method has to called to initialize annotated fields. In above example, initMocks() is called in #Before (JUnit4) method of test's base class. For JUnit3 initMocks() can go to setup() method of a base class.
So that in above question you have used the SpringJUnit4ClassRunner and MockitoAnnotations.initMocks(this); it will create two mock bean references for which ever you use the #Mock. Also in above code flow.
1.At the beginning the SpringJUnit4ClassRunner run it will create bean reference for #Mock and #MockBean annotated attributes.after that it will create injected bean at tjis time only happens the constructor injection
2.Run #Before and Run MockitoAnnotations.initMocks(this); it will create another mock references for #Mock annotated attributes and replaced direct reference only. and Auto wired injection run at this time.
After running the MockitoAnnotations.initMocks(this); you will see the allk beans are initialized and injected well. But beans which are injected thruogh constructor those beans are not correct reference these are referring old bean reference which are created by SpringJUnit4ClassRunner.
If you want to achieve the constructor and Auto wired injection for a single bean you must to use manual bean injected for the constructor injection. you can refer here

passing properties field using #value annotation to mocked method is null

I am unable to pass the field by reading from application-test.properties file from test to the mocked method.
#RunWith(SpringRunner.class)
#TestPropertySource("classpath:application-test.properties")
public class ReportImplTest {
#Mock
private Dependencies dependencies;
#InjectMocks
private ReportImplTest underTest;
#Test
public void testgetReports() {
List<String> reps= underTest.getReports(anyString());
}
}
Here is the actual class of the mocked method
#Component
public class ReportImpl {
#Value("${REP_PROPNAME}")
String reppropname;
public List<String> getReports(String rep){
return staticUtilityclass.process(staticUtilityclass.transform(reppropname,"Reports"));
}
}
reppropname is coming as null in the getReports method. Test is executing in test context wheres the ReportImpl class will be in application context. Is there a way to get the value of the reppropname.
I tried used #ContextConfiguration (#ContextConfiguration(classes={ApplicaitonBootStarter.class)}
it is working , but it loads all the beans and dependencies.
Any other way to get the reppropname?
The reason why the value is not injected here is that you don't provide the configuration to your test class. Spring just doesn't know how to build your bean.
So, as you mentioned you have to annotate the test class with #ContextConfiguration. If you don't want to build the entire context with all the beans, you can provide create a test configuration and provide there only the needed beans.
#Configuration //can be as well annotated with #TestConfiguration
#ComponentScan("package.to.scan")
public class TestConfiguration {
}
And now provide this class to your test
#RunWith(SpringRunner.class)
#TestPropertySource("classpath:application-test.properties")
#ContextConfiguration(classes = TestConfiguration.class)
public class ReportImplTest {
........
}
But there is one more thing. Assuming that you have a #Before method that performs MockitAnnotations.initMocks(this);, you still have your object-under-test declared only with #InjectMocks. What does it mean? It means that if you don't initialize this object by yourself, mockito will take care of it and will initialize with using the available constructor, and in this case, spring won't inject the #Value annotated field. What you need to do, is to annotate you object-under-test with #Autowired so spring will initialize it before mockito will try to take care of it:
#InjectMocks
#Autowired
private ReportImplTest underTest;

Mockito: How to mock field dependencies partially in spring bean

I have a Spring service with multiple field dependencies as below. One of the dependency (thirdPartyService) communicates with an external application. How can I just mock that?
#Service
public class PlannerServiceImpl implements PlannerService {
private static final Logger LOG = LoggerFactory.getLogger(PlannerServiceImpl.class);
#Autowired
private RepositoryA repositoryA;
#Autowired
private RepositoryB repositoryB;
#Autowired
private ThirdPartyService thirdPartyService ;
}
If I use Mock annotation then it still connects to external application instead of returning mock response:
#Mock
ThirdPartyService thirdPartyService;
#Autowired
PlannerService plannerService;
And If I use InjectMocks annotation then it gives NullpointerException for RepositoryA and RepositoryB.
#Mock
ThirdPartyService thirdPartyService;
#InjectMocks
PlannerService plannerService = newPlannerService();
How can I just Mock ThirdPartyService and let Spring inject other dependencies?
You can modify what Spring has injected using Whitebox. Optionally, since you are using Spring, you can also use ReflectionTestUtils.setField
After Spring injects the dependencies, and before your unit test runs, you can use org.powermock.reflect.Whitebox to modify Spring's injection. Something like this
Whitebox.setInternalState(plannerService, "thirdPartyService" thirdPartyService);
Where thirdPartyService is your mocked instance.
javadoc here
or using Spring's ReflectionTestUtils:
ReflectionTestUtils.setField((plannerService, "thirdPartyService" thirdPartyService);
java doc here
This can typically be done in your "setup" method, the one annotated with #Before.
You can have a setter method in your PlannerService class under test
void setRepositoryA(RepositoryA repository) {
this.repositoryA = repository;
}
And then use this method in your test to supply a mock implementation of the RepositoryA
Or you can #Inject the repository in the constructor and then in your unit test call the constructor with the mocks as the arguments, instead.
The #Autowiring and #InejctMocks can be used in a test case for sure (annotations being used separately on different instance fields).
Make sure you:
1) Initiate Mockito mocks in the #Before method:
#Before
public void before(){
MockitoAnnotations.initMocks(this);
}
2) Use SpringJUnit4ClassRunner.class in the #RunWith class annotation

Make a class to be a Mockito mock without calling mock

I have a Java application based on Spring that uses services. For some reasons, I need to autowire a service that is implemented just for the sake of unit testing. I would like this autowired class to be a Mockito mock, such that I can pass it to all the Mockito methods (when(), etc.). Should I extend or implement some Mockito class?
E.g.
#Profile("test")
#Primary
#Service
public class MockAService implements AService {
public void callMethod(){}
}
Then in the test I have
{
System.setProperty("spring.profiles.active", "test");
}
#Autowired AService aservice;
and in the test method I want to do:
#Test
public void test(){
doNothing().when(aService).callMethod();
}
Just use #Spy and in you #Before method call MockitoAnnotations.initMocks. It will create a spy for your injected services.
Exemple i want to test MyService and it has a dependecy on MyDao. I want MyService to use my mocked dao.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:/com/stackoverflow/mokito/exemple/spring.xml"})
public class TestCase {
#Resource
#InjectMocks
private MyService service;
#Resource
#Spy
private MyDao dao;
public void setup() {
MockitoAnnotations.initMocks(this);
}
}
However your spring context is now "dirty". The next test class will use the same spring context and MyService will still use the mock. So don't forget to reset mock in #After
A mock would even be better than a spy if you don't want the service to do anything. By default, calling the function on the mock won't do anything.
With the spy, you have to explicitly call doNothing().
But why do you want to load any Spring context anyway ? Couldn't you mock all the services and test your class in full isolation ?

Tests injecting beans with #Autowired annotations

I want to write unit test using Junit and Mockito, in this case I don't want to write integration tests. The method that I want to test uses variables that are injected via Spring using the #Value or #Autowired annotations. How can I populate the injected variables so that when I run a test they are not null. Before days of annotations I would have created mocked classes of the variables and set them via setter methods.
I'm writing unit tests so I would prefer not to use #RunWith(SpringJUnit4ClassRunner.class).
You can use the MockitoJUnitRunner.
class SystemUnderTest {
#Autowired
private Dependency dep;
// ...
}
#RunWith(MockitoJUnitRunner.class)
public class YourTest {
#Mock
private Dependency mockDependency;
#InjectMocks
private SystemUnderTest testee;
#Test
public void testSystem() {
// at this point testee is already injected with mockDependency
}
}

Categories