Mockito void method how to inspect private variable? - java

I am trying to unit test a void method which gets data from database, formats into map and passes onto another class.
I have mocked out the database call and returned my own data, and I want to inspect the map which has been formatted contains the right number of elements and keys in right locations.
I have tried playing around with argumentCaptor to do this but struggling to get my head round it.
Method to test:
public class MyClass {
#Autowired
private Dao dao;
#Autowired
private AnotherClass anotherClass;
public void format(String, Date) {
Map<Object,Object> map = getDataAndFormat(String, Date);
anotherClass.doSomething(map);
}
private Map<Object, Object> getDataAndFormat(String, Date) {
Map map;
if (String.equals("A") {
map = dao.getData(Date);
}
else {
map = dao.getDataSomeThingElse(Date);
}
}
}
Any help much appreciated
Thanks,
so: This is what I have so far:
#InjectMocks
#Autowired
//class to test
private MyClass myClass;
#Mock
Dao dao;
#Captor
private ArgumentCaptor<String> argumentCaptor;
#Captor
private ArgumentCaptor<Date> argumentCaptorB;
public void testFormat()
{
when(dao.getData(Matchers.any())).thenReturn(data());
myClass.format("A",new Date());
}
So i want to use argument captors (but not entirely sure how to) to get the map from the format method call and inspect the returned map. The code i currently have is hitting all my code but I can't assert on anything hence why i wanted to check the map contains what I expect it to. Hope that makes sense

It appears that what you want to do is to confirm the contents of the map that is passed to AnotherClass.
If AnotherClass is an interface, you can create a mock instance of AnotherClass and inject it into your MyClass. You can then verify that it has been called with your arguments of choice:
Mockito.verify(mockAnotherClass).doSomething(expectedMap);

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.

How to mock a map in mockito?

I have two maps that have the same arguments. I would like to mock one of them to test my class. But I don't know a reason that it's not working
this is my class
public class A {
private Map<String, Foo> map1;
private Map<String, Foo> map2;
public A() {
this.map1 = new HashMap<String,Foo>();
map1.put("one",new Foo());
this.map2 = new HashMap<String, Foo>();
map2.put("two", new Foo());
}
public void doSomenthing(String str){
Foo foo = map1.get(str)
//other actions
}
}
and this is my test class:
public class ATest{
#InjectMocks
private A a;
#Mock
private HashMap<String, Foo> mapTest;
#Before
public void initialize() throws Exception {
when(mapTest.get(Mockito.anyString())).thenReturn(new Foo());
}
#Test
public void testSomething() throws Exception {
a.doSomething("blabla");
}
}
#InjectMocks tries to inject the dependencies in following ways
By using construtor first.
Then property setter.
Then field injection.
#3 is probably the way for you. Try the following:
Remove map initialization from constructor to their setter function.
Change the variable name mapTest to map1 in your test class.
also define map2 similarly.
Then InjectMocks should find a matching field to inject.
Share more parts fo the code for a more precise answer.
Before you jump in mock the Map, is that necessary to mock a map? Mock is used to replace other part of your code that you don't want to be involved in your unit test. While Map is easy enough to initiate in unit test.
You need the same name and same type in the two classes:
//main class
private HashMap<String, Foo> map;
//test class
#Mock
private HashMap<String, Foo> map;

How to check test cases if no verification possible

I'm learning about unit testing and I came across the problem to create a final check on wether a test case is correct or not. Usually I try to create a verification, like through an assertEquals(). But what is recommended to do when it's not possible to test it like this?
I have a class like this:
public class Landlord {
private Map<String, ChannelHandlerContext> currentOccupier;
private static Landlord instance;
public Landlord() {
currentOccupier = new HashMap<>();
}
public static Landlord getInstance {
//return instance
}
public void add(Occupier occupier){
currentOccupier.put("test", occupier.getChannelHandlerContext());
}
}
And now I try to test the method like this:
public class LandlordTest {
private Landlord landlord;
#Mock
private Occupier occupier;
#Mock
private ChannelHandlerContext channelHandlerContext;
#BeforeEach
void setUp() {
occupier = mock(Occupier.class);
channelHandlerContext = mock(ChannelHandlerContext.class);
landlord = Landlord.getInstance();
when(occupier.getChannelHandlerContext()).thenReturn(channelHandlerContext);
}
public void add(Occupier occupier){
addedOccupier.put(occupier.getChannelHandlerContext());
//adding succeded
}
}
Maybe in this short example it wouldn't be needed to test it, but is there a way to verify that the add method was successful? Normally in these kind of cases, I'd try something like: assertEquals(currentOccupier.size(), 1), but here I can't access the hashMap of the instance to do it like this. Is there another way to verify the correct behaviour of adding it?
This assertEquals(currentOccupier.size(), 1) is really not enough.
You want to assert that the map contains the entry that you added in the map.
That assertion is too shallow : it doesn't check for the entry neither the value of the key nor the value of the value.
You should do something like :
ChannelHandlerContext actualContext = landLord.get("test");
assertSame(addedContext, actualContext);
// or assertEquals if the instances may differ because you do some defensive copy in add()
Note also that you mock here some things that should not need to be mocked : occupier and channelHandlerContext make part of your model. You should be able to provide "normal" instances of them in the frame of the test.
Here you have broadly two ways to perform that :
1) adding a public method in the class to under test to find an ChannelHandlerContext :
public ChannelHandlerContext get(String name){
currentOccupier.get(name);
}
Do that only if providing this access is acceptable.
If you cannot add a public method, add a package level method as this doesn't make part of the exposed API.
2) use reflection api (essentially Class.getDeclaredField(String) and Field.get()) to retrieve the map instance from the instance under test and then assert that it contains the expected ChannelHandlerContext instance for the "test" key.

How to deal with side effects of mocked method with JUnit/Mockito

This might be a dumb question or something obvious to figure out, but nevertheless, I'm really struggling to solve this.
Let's say we have the following logic within a method under test:
#Service
public class ServiceToTest() {
#Autowired
private SomeService someService;
public String testMethod() {
Map<String, String> parameters = new HashMap<String, String>();
// eventParameters will be populated inside someService
String result = someService.doLogic(parameters);
// do something with the result here that doesn't really matter for this example
String name = parameters.get("name").toLowerCase();
return name;
}
}
Inside SomeService the parameters map is populated with some values, like the "name" in this example. I would like to mock this service in my unit test.
Consider the following unit test snippet:
#RunWith(SpringRunner.class)
public class ServiceToTestTest {
#TestConfiguration
static class ServiceToTestConfiguration {
#Bean
public ServiceToTest serviceToTest() {
return new ServiceToTest();
}
#Autowired
private ServiceToTest serviceToTest;
#MockBean
private SomeService someService;
#Test
public void testShouldReturnJimmy() {
given(someService.doLogic(Mockito.anyMap())).willReturn("whatever");
String name = serviceToTest.testMethod();
assertThat(name).isEqualTo("jimmy");
}
}
When I execute this test I get a NullPointerException on this line:
String name = parameters.get("name").toLowerCase();, which makes sense as the method that should populate this map is mocked and parameters.get("name") is null. Let's also assume that I really want to have a String returned from doLogic(parameters), so it cannot be the parameters map.
Is there a way to somehow instruct the mock object to populate the parameters map, or to mock the map object itself?
(The code examples here were written for this post on the fly, so please forgive me if there are any stupid mistakes that I haven't noticed while writing them ;-) )
Can be done using the controversial thenAnswer method.
https://static.javadoc.io/org.mockito/mockito-core/2.13.0/org/mockito/stubbing/OngoingStubbing.html#thenAnswer-org.mockito.stubbing.Answer-
But JB's comment is correct. This is not a great idea.

How to mock a method call within a method for which i am writing junit test

Could someone help me with mocking a method call within a method
my code is like :
public class Service {
public List<Bean> Filter(Bean bean){
List<Bean> Filtered_List = getUtilityService.getBeanList();
//Do something
return beanList;
}
}
Now i want to write test case for Service class . How can i mock :
List Filtered_List = getUtilityService.getBeanList(); and set values in it.
The clean solution is to extract UtilityService to a field and pass a mock to the constructor.
public class Service {
private UtilityService utilityService;
public Service(UtilityService utilityService) {
this.utilityService = utilityService;
}
public List<Bean> Filter(Bean bean){
List<Bean> filteredList = utilityService.getBeanList();
//Do something
return beanList;
}
}
You can also introduce a UtilityServiceFactory and have a utilityServiceFactory field in the Service.
public class Service {
private UtilityServiceFactory utilityServiceFactory;
public Service(UtilityServiceFactory utilityService) {
this.utilityServiceFactory = utilityServiceFactory;
}
public List<Bean> Filter(Bean bean){
List<Bean> filteredList = utilityService.create().getBeanList();
//Do something
return beanList;
}
}
If getUtilityService is located in Service class, there is also a dirty solution: partial mock. But I do not recommend it. It's better to refactor your code and use one of previous approaches.
EDIT:
Constructor injection with #InjectMocks is not the best idea but here you are:
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.BDDMockito.given;
//other imports
#RunWith(MockitoJUnitRunner.class)
public class ServiceTest {
#Mock
UtilityService utilityService;
#InjectMocks
Service service = new Service(null);
#Test
public void shouldFilterBeans() throws Exception {
//given
given(utilityService.getBeanList()).willReturn(asList(new Bean()));
//when
List<Bean> result = service.filter(new Bean());
//then
assertThat(result).hasSize(1); //probably you want to check something else
}
}
to test a method including its own parameter and return value, like the Filter method in your code, it's enough to just pass a Bean instance to it, and then assert the returned List<Bean> object equals to your expected result. generally, for this kind of method, i think it's no need to use mock frameworks.
but if you really want to test the getUtilityService().getBeanList() method call, you should refactor your code:
addfield UnitilityService service and its corresponding setter method in your class Service
in your unit test code, inject a mocked service using the setter method to the object under test and given a returning value for its getBeanList() method, then invoke your Filter method, finally, verify the method call. for the detailed implementation, you can refer the answer of #woru.

Categories

Resources