Reputation: 83
Here I am trying to mock autowire fields ServiceHelper of Service class TestServiceImpl , I am not able to call method through mock object of ServiceHelper class.
This is my class files:
@Service
public class TestServiceImpl implements TestService {
@Autowired
private TestDAO testDAO;
@Autowired
private ServiceHelper serviceHelper;
@Override
public ResultsModel getResults(Map<String, Object> map) throws WebServiceException_Exception {
return serviceHelper.getResults(map);
}
2nd Class:
@Repository
public class ServiceHelper {
private static Logger logger = Logger.getLogger(ServiceHelper.class.getName());
@Autowired
ResponseHeader responseHeader;
public void setResponseHeader(ResponseHeader responseHeader) {
this.responseHeader = responseHeader;
}
public ResultsModel getResults(Map<String, Object> map) throws WebServiceException_Exception {
....
}
And Test class:
@RunWith(MockitoJUnitRunner.class)
public class MockitoTester {
@InjectMocks
private TestServiceImpl serviceImpl = new TestServiceImpl();
@Mock
private TestDAO testDAO;
@Mock
private ServiceHelper sHelper;
@Before
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
@Test
public void testResult() throws Exception {
Map<String, Object> map = new HashMap<>();
map.put("TestId", "test123");
map.put("lang", "en");
map.put("cntry", "USA");
ResultsModel results = new ResultsModel();
when(sHelper.getResults(map)).thenReturn(results);
results = serviceImpl.getResults(map);
Assert.assertEquals(results.getStatus(), "Success");
}
Here in my test class:
results = serviceImpl.getResults(map);
It goes to TestServiceImpl class to method :
public ResultsModel getResults(Map<String, Object> map) throws webServiceException_Exception {
return serviceHelper.getResults(map);
}
but at point :
serviceHelper.getResults(map);
it is not going inside serviceHelper.getResults(map)
and return all values as Null.
Please suggest where I need to do changes.
Upvotes: 8
Views: 28980
Reputation: 41271
You have three choices here:
Option 1 is really integration testing -- you can annotate your test class with @RunWith(SpringRunner.class)
and use more Spring annotations to control dependency injection. It's too big a subject to cover in a SO answer, but there are plenty of examples if you Google for "spring integration test mockito".
But for unit testing, I think it's better not to involve Spring. A good Spring bean doesn't need Spring to function. Option 2 just says, write your class so that unit tests (and anything else) can inject the dependency (be it a mock, or anything else) through normal Java means.
Constructor injection is cleanest in my opinion:
private final ServiceHelper serviceHelper; // note: not annotated
@Autowired
public TestService(ServiceHelper serviceHelper) {
this.serviceHelper = serviceHelper;
}
But you can also do this with a public void setServiceHelper(ServiceHelper helper)
-- this is less good because the field can't be final
.
Or by making the field public
-- I assume you know the reasons this is bad.
If you're determined to have a private field that's not set by a public constructor or setter, you could use Spring's ReflectionUtils.setField()
from within your test:
@Mock
private ServiceHelper serviceHelper;
private TestService service;
@Before
public void configureService() {
service = new TestService();
Field field = ReflectionUtils.findField(TestService.class, "serviceHelper");
ReflectionUtils.setField(field, service, serviceHelper);
}
(Or, equally, use JDK's reflection classes directly, or reflection utils from elsewhere)
This is explicitly using reflection to subvert the access rules you've coded into the class. I thoroughly recommend option 2.
Upvotes: 8
Reputation: 2852
Try using constructor injection it'd be easier to mock the classes for testing... here's an example on how I would structure my classes to get you going. When you write your tests you now have to pass the Mocked object into the instance you're creating of these classes:
@Service
public class TestServiceImpl implements TestService {
private TestDao testDao;
private ServiceHelper serviceHelper;
@Autowired
public TestServiceImpl(TestDAO testDAO, ServiceHelper serviceHelper) {
this.testDAO = testDAO;
this.serviceHelper = serviceHelper;
}
}
@Repository
public class ServiceHelper {
private ResponseHeader responseHeader;
@Autowired
public ServiceHelper(ResponseHeader responseHeader) {
this.responseHeader = responseHeader
}
}
Upvotes: 2
Reputation: 9612
I think the issue may be that you are stubbing your method to return the same object which you then assign the result of the method under test. i.e. (the results
object here):
ResultsModel results = new ResultsModel();
when(sHelper.getResults(map)).thenReturn(results);
results = serviceImpl.getResults(map);
This will probably cause some sort of cyclic confusion when it tries to stub the method in Mockito, and it certainly won't make your assertation pass:
Assert.assertEquals(results.getStatus(), "Success");
Since the status on results is never set anywhere.
I think you need to make separate objects for your stubbing and your returned value from the method under test and make sure you set your stubbed one to have a status of "Success":
ResultsModel results = new ResultsModel();
results.setStatus("Success");
when(sHelper.getResults(map)).thenReturn(results);
ResultsModel returnedResults = serviceImpl.getResults(map);
Assert.assertEquals(returnedResults.getStatus(), "Success");
Upvotes: 2