Test class with nested dependencies - java

I'm testing a class with nested (Autowired) dependencies. The class implements businesslogic for making alterations in the backend. Specifically the test should assert that when a certain backend call returns an error:
No more backend calls are made
The response object returns an error
I dont know how the do the latter.
My class looks something like this:
public class Handler {
#Autowired
private DaoImpl dao;
#Autowired
private SpecificUtil util1;
#Autowired
private GeneralUtil util2;
#Autowired
private Helper helper;
public Response doSomethingClever(Request request) {
// calls to dao
// logic with help of util and helper classes
}
}
The testclass:
public class HandlerTest {
#Spy
private DaoImpl dao;
#Mock
private SpecificUtil util1;
#Mock
private GeneralUtil util2;
#Mock
private Helper helper;
#InjectMocks
Handler handler;
#Test
public void testDoSomethingClever() {
// set up dao to fail
QueryResult error = getErrorResult();
org.mockito.Mockito.when(dao.queryBackEnd(any(SpecificQuery.class))).thenReturn(error);
// perform query
Request request = getTestRequest();
Response errorResponse = handler.doSomethingClever(request);
// verify that either:
// Response has errors - fails
// because helper classes are mocks, have not set the error
assertNotNull(response.getErrorMessage());
// the method setErrors of Response was called once - fails
//because the setError was called earlier!
Response spyResponse = Mockito.spy(errorResponse);
verify(spyResponse, times(1)).setError(anyString);
//verify no other calls are made except the queryBackEnd call - this part works
org.mockito.Mockito.verify(dao).queryBackEnd(any(SpecificQuery.class));
org.mockito.Mockito.verifyNoMoreInteractions(dao);
}
}
The Response object is created in the Handler class. If i check the returned response, no interactions will be recorded by Mockito, because the interactions have taken place prior to calling Mockito.spy.
I have tried making it an integration test, by using #Spy instead of #Mock. The idea is to instantiate all the nested dependencies except for the dao and get a proper Response to test for errors. But this does not work because some of the #Autowired helper and utility classes also have #Autowired dependencies and these nested dependencies are not instantiated during testing.
Is there any way to inject #Spy objects into other #Spy objects with Mockito?
Or is there some other solution in this case? Sould i write my own mockobjects?

A unit test should test the code of a specific unit only (here the Handler class). This includes interacting with the dependencies.
From what you wrote in your question and comments your handle method looks something along these lines:
public class Handler {
#Autowired
private DaoImpl dao;
#Autowired
private Util util;
public Response doSomethingClever(Request request) {
SpecificQuery specificQuery = new SpecificQuery();
specificQuery.setSomeData(request.getSomeData());
IntermidiateResponse intermidiateResponse = dao.queryBackEnd(specificQuery);
Response response = util.processIntermidiateResult(intermidiateResult);
return response;
}
}
There are a few interactions to unit test here:
Given a Request instance assert that DaoImpl::queryBackEnd method is called with a SpecificQuery instance that has someData property set to someData property from the Request object
Given a mocked IntermidiateResponse being returned from the DaoImpl::queryBackEnd method assert that this result is passed on to the Util::processIntermidiateResult method
Given a mocked Response being returned from the Util::processIntermidiateResult method assert that this is exactly what gets returned from the handle method
This way you have 100% coverage on the Handler::handle method. If you have some more calls in the response processing pipeline you test them all accordingly.
Hope this answers your question. Good luck

There are two options, one is to test the Handler in isolation, mocking everything else. See the answer by jannis. The other option is to control/mock the in- and output of the Handler, and treat the Handler and all its utility classes as a black box.
I have opted for this last option because it means that i can refactor the utility-classes, and the Handler class itself, and that tests will succeed as long as i don't change what the Handler does. It does mean that i have departed somewhat from doing unit testing and that i'm really doing more of an integration test.
For this i mock the Dao class, so that i can have it return an error at the desired point and so that i can assert that no further calls are made after the error.
It is the only class i mock, so i need to inject it into the Handler. This is possible with Springs ReflectionTestUtils (I did'nt know about this yesterday)
The testcode then becomes shorter:
public class HandlerTest {
#Autowired
private Handler handler;
#Test
public void testDoSomethingClever() {
// set up dao to fail
Dao mockDao = org.mockito.Mockito.mock(DaoImpl.class);
QueryResult error = getErrorResult();
org.mockito.Mockito.when(dao.queryBackEnd(any (SpecificQuery.class))).thenReturn(error);
// inject the dao
ReflectionTestUtils.setField(handler, "dao", mockDao);
// perform query
Request request = getTestRequest();
Response errorResponse = handler.doSomethingClever(request);
// verify that Response has errors
assertNotNull(response.getErrorMessage());
//verify no other calls are made except the queryBackEnd call
org.mockito.Mockito.verify(dao).queryBackEnd(any(SpecificQuery.class));
org.mockito.Mockito.verifyNoMoreInteractions(dao);
}
}

Related

Mockito Junit on a reference object

I am facing a strange problem while trying to unit test my code.
Here is my code :
public class ItemService {
private OfferService offerService;
#Inject
public ItemService (OfferService offerService){
this.offerService = offerService;
}
public List<Item> buildItems(ItemInfo itemInfo) {
List<Item> items = processItem(itemInfo);
Offers<Offer> offers = fetchItemInfo(items);
// based on the object offers, do some processing
}
private Offers<Offer> fetchItemInfo(List<Item> items) {
Offers<Offer> offers = new Offers<>();
// some processing here with offers.
// calling the db to fetch details
offerService.fetchInfoFromDB(offers);
return offers;
}
}
public class OfferService {
public void fetchInfoFromDB(Offers<Offer> offers) {
// fetching details from DB
// and updating the object **offers**
myDao.getDetailsById(id);
}
}
Now I have written junit to test the method buildItems()
UPDATE updating the mocks used and mock injection.
#RunWith(PowerMockRunner.class)
#PrepareForTest(ItemService.class)
public class ItemServiceTest{
#Mock private MyDAO myDao;
#Mock private OfferService offerService;
#Before
public void setUp() throws Exception {
ItemService itemService = new ItemService (offerService, myDao);
}
public void testBuildItems(){
// some code -----
itemInfo = buildItemInfo();
offerDetail = buildOfferDetail();
when(myDao.getDetailsById(Mockito.anyLong())).thenReturn(offerDetail);
// some code -----
// I need to implement some code which will actually call
// offerService.fetchInfoFromDB(offers);
// and update the --offers-- object and return it.
List<Item> items = itemService.buildItems(itemInfo);
Assert.assertNotNull(items);
}
}
I am running with coverage and I can see that the below line got executed but the actual method is not getting called :
offerService.fetchInfoFromDB(offers);
I am getting null values in offers. Then I added the below line :
doCallRealMethod().when(offerService).fetchInfoFromDB(offers);
Still the same result. The offers object is passed by reference and is getting updated after the DB call which I am mocking already. But upto that call my code is not reaching. How can I update the offers object in my junit. Please help.
Your test is calling a zero arg ItemService() constructor, not the one arg #Inject constructor you posted. Either your code won't compile, or you haven't actually shown us the code in question.
Also, you say you are mocking offerService:
You call when on myDao and not offerService,
you do not pass your mock offerService into your ItemService constructor, as in new ItemService(offerService), and
your doCallRealMethod won't work because your mock offerService won't use your mock myDao; you'll need to mock the call on offerService directly with a thenAnswer that changes the passed List<Offer>, as on my question you linked.
doAnswer(invocation -> {
((List<Offer>) invocation.getArgument(0)).add(offerDetail);
return null;
}).when(offerService).fetchInfoFromDb(any());
If you fix those three you will be considerably closer to a working test.

Mockito calls stubbed method even if using doReturn

I want to create an integration test where a put method is called on a controller and it updates a certain object. During this process, a service class is involved which calls a third party API to do some stuff. In my case, I want to stub the service method which is involved in calling the third party as it is not the point to test the third party.
Having that said, I will present my code and I wait for an answer about why this does not work as expected and/or any other workaround.
This is my service class in which is the method call I want to stub.
public class ProjectService implements SomeInterfance {
// the third party service
private final DamConnector damConnector;
// some other fields
public ProjectDTO save(ProjectDTO projectDTO) {
log.debug("Request to save Project : {}", projectDTO);
// some operations
synchronizeWithDamAndSave(project, parentChanging); //this is the method call I want to be skiped
//other operations
return projectMapper.toDto(project, this);
}
//the method that I want to stub
public Asset synchronizeWithDamAndSave(Project project, boolean includeDocuments) {
Asset asset = synchronizeWithDam(project, includeDocuments);
projectRepository.save(project);
return asset;
}
}
And my integration test class:
#SpringBootTest(classes = SppApp.class)
public class ProjectResourceIT {
//other fields
//my service use autowire as it needs to make the service calls
#Autowired
private ProjectService projectService;
//this is my setup method where I create the spy of project service and define the doReturn behavior when my method is called
#BeforeEach
public void setup() {
ProjectService spyProjectService = Mockito.spy(projectService);
Mockito.doReturn(new Asset()).when(spyProjectService).synchronizeWithDamAndSave(Mockito.any(Project.class),Mockito.anyBoolean());
MockitoAnnotations.initMocks(this);
final ProjectResource projectResource = new ProjectResource(spyProjectService, clientService, securityService);
this.restProjectMockMvc = MockMvcBuilders.standaloneSetup(projectResource)
.setCustomArgumentResolvers(pageableArgumentResolver)
.setControllerAdvice(exceptionTranslator)
.setConversionService(createFormattingConversionService())
.setMessageConverters(jacksonMessageConverter)
.setValidator(validator).build();
}
}
...
public void updateProject() throws Exception {
// initialization of the test
// this is where I call my controller
restProjectMockMvc.perform(put("/api/projects")
.contentType(TestUtil.APPLICATION_JSON_UTF8)
.content(TestUtil.convertObjectToJsonBytes(projectDTO)))
.andExpect(status().isOk());
}
}
The problem in my case is that mockito enters in synchronizeWithDamAndSave method just after
Mockito.doReturn(new Asset()).when(spyProjectService).synchronizeWithDamAndSave(Mockito.any(Project.class),Mockito.anyBoolean());
this line is called, before the method to be called from the rest api.
What should I do? Any hints about why this is happening?
Spring Boot's proxies are not working wit Mockito. Use #SpyBean instead of #Autowired.

Getting Missing 1 invocation to... in

I have a test that is testing a Spring #Service class. This service class autowires in a dao #Repository. The dao also autowires in a class to provide extra functionality. I would like to mock the calls in the dao so I have something like so:
#DisplayName("Tests for ...")
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class MyServiceTest
{
#Injectable
TheRepository myDao;
#Tested(fullyInitialized = true)
TheService myServiceUnderTest;
#Nested
#Tag("...")
#DisplayName("Tests for method: ...")
class TestClassContainer {
#Test
public void test1() {
IAnalysisDataScenario data = new CommonAnalysisDataScenario();
new Expectations() {{
myDao.nameExists(anyString); result = data.mockedNameExists();
...
}};
Map<String, Object> result = myServiceUnderTest.getAnalysis(data.getName(),data.getId());
assertTrue(!result.isEmpty());
}
}
}
The call to getAnalysis in the service calls the dao method nameExists which I want to return the result from mockedNameExists in the Expectations block but I can't figure out what I'm doing wrong.
This is what I see when I run the test:
Missing 1 invocation to:
...dao.impl.TheRepository#nameExists(any String)
on mock instance: ...dao.impl.TheRepository#61230f6a
Caused by: Missing invocations
at ...dao.impl.TheRepository.nameExists(TheRepository.java)
I see what the issue is in this case. The error message now makes sense. In this particular test, the method that is missing the invocation is not actually called because of the code path the test takes based on the the parameters. So the Expectations block must only contain mocking behavior for code that actually WILL BE EXECUTED. Now I understand

How to write mockito test cases for factory class

I was new for writing test cases. I want to write the Mockito test cases for the below :
#Component
public class FactoryClass {
#Autowired
private DataService dataService;
#Autowired
private ModelMapper modelMapper;
public List<TestEntity> convertEventToEntity(CEvent cEvent, Event event){
List<TestEntity> TestEntityList =new ArrayList<>();
if (!CollectionUtils.isEmpty(cEvent.getOrderDetails())) {
for (CEventDetail cEventDetail : cEvent.getCEventDetail()) {
log.info("Creating TestEntity entity ..");
TestEntity testEntity = new TestEntity();
testEntity.setEId(event.getEId());
testEntity.setActId(event.getHeaderReference().getActId());
testEntity.setEName(event.getEType());
tfbUpgrades.setPO(cEventDetail.getPO());
TestEntityList.add(testEntity);
}
}
return TestEntityList;
}
}
Can anyone please help me with the code sample to write mockito test cases for the factory class.
The only thing you might want to test in this class is the public convertEventToEntity method. The class has two dependencies - dataService and modelMapper. But they are not used in the code, so you don't need to mock them. So you can write tests for this class without using Mockito. Just create an object of FactoryClass and call the method with some input CEvent and Event objects. You can assert on expected List<TestEntity>.
You can test several logical paths in this method. For example, there's an if condition that does something only if cEvent's order details are present. So you can have two test cases where you pass cEvent with and without order details and you can verify that the code is executed correctly in both cases.

how to mock an interface using PowerMockito in Java

req1(an object of request type 1)
req2(an object of request type 2)
class response{
public TxnResp txnResp(){
TxnResp txnResp = service.txn(req1 , req2);
return txnResp;
}
}
I am using Powermockito for mocking in my junit testing.
"txn" is an interface. I need to mock this line TxnResp txnResp = service.txn(req1 , req2); my test class. Because calling txn is returning some value's from the webservice, which are specified in the next line.
txnResp contains following values storeNo,retailerId and password. It has its own bean class, so that we can set values like txnResp.setStoreId(1);
Could any one please help me to mock the above interface 'txn' and return value to txnResp.
I am stuck for the last 5 hours in it. Any help will be highly appreciated.
In my opinion plain Mockito would be enough in your situation.
If service is a dependency that is injected (via constructor, setter i.e.), you could try something following:
public class ResponseTest{
#InjectMocks
private Response response;
#Mock
private Service serviceMock;
#Before
public void init(){
MockitoAnnotations.initMocks(this);
}
#Test
public void test(){
// Arrange
TxnResp txnResp = new TxnResp(...);
Mockito.doReturn(txnResp).when(serviceMock).txn(
Mockito.any(ReqType1.class), Mockito.any(ReqType2.class));
// Act and assert...
}
}

Categories

Resources