Mocking a List and attempting to iterate over it - java

Currently using Mockito to Test a method from one of my classes. My class contains a List, and the method takes in an object of the same class. The issue is when I attempt to iterate over the List from the object. I'm getting a null pointer to the list. Below you will see the code snippets.
private Shipment shipment;
private Shipment shipment2;
#Mock
private Order mockOrder1;
#Mock
private Order mockOrder2;
#Mock
private Order mockOrder3;
#Mock
private ArrayList<Order> mockShipmentOrders;
#Mock
private ArrayList<Order> mockShipmentOrders2;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
mockShipmentOrders.add(mockOrder1);
mockShipmentOrders.add(mockOrder2);
mockShipmentOrders2.add(mockOrder3);
shipment = new Shipment(1, mockShipmentOrders);
shipment2 = new Shipment(2, mockShipmentOrders2);
}
#Test
public void test_mergeShipments_increasesByOneWhenAShipmentOfOneAddedToAShipmentORderSizeOfTwo(){
shipment.mergeShipments(shipment2);
assertEquals(3, shipment.getShipmentOrders().size());
}
above you can see my mockito test and below is my Class with the method:
public class Shipment {
private long shipmentID;
private List<Order> shipmentOrders;
public Shipment(long shipmentID, List<Order> shipmentOrders){
this.shipmentID = shipmentID;
this.shipmentOrders = shipmentOrders;
}
public List<Order> getShipmentOrders(){
return shipmentOrders;
}
public void mergeShipments(Shipment shipment2){
List<Order> existingShipment = shipment2.getShipmentOrders();
for (Order order : existingShipment){
shipmentOrders.add(order);
}
}
When I run the test I am getting a java.lang.NullPointerException for the line: for (Order order : existingShipment){
in mergeShipemts();
The question is; Is it possible to mock a list, call that list and then run a foreach over that mocked list?

There are some fundamental issues why your example not does work and throws a NullPointerException.
The call to add() on the mocked list effectively doesn't do anything. All void methods on mocks are "no-ops" by default
Iterating over a list using for-each syntax calls Collection.iterator() under the hood. This returns null, because you've not setup mockito to return anything else.
Instead, I would not mock the list and instead pass an actual list. Arrays.asList() is convenient for testing.
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
shipment = new Shipment(1, Arrays.asList(mockOrder1, mockOrder2));
shipment2 = new Shipment(2, Arrays.asList(mockOrder3));
}
If you're determined to mock a list then you'll have to mock its behaviour, i.e. making the add() actually store something and .iterator() return an iterator. This can be done rather painfully as follows. I've only included this to demonstrate the principle.
#Mock
private List<String> mockedList;
#Before
public void init() {
MockitoAnnotations.initMocks(this);
List<String> realList = new ArrayList<>();
doAnswer(new Answer<String>() {
#Override
public String answer(InvocationOnMock invocation) throws Throwable {
realList.add(invocation.getArgumentAt(0, String.class));
return null;
}
}).when(mockedList).add(any());
when(mockedList.iterator()).thenAnswer(new Answer<Iterator<String>>() {
#Override
public Iterator<String> answer(InvocationOnMock invocation) throws Throwable {
return realList.iterator();
}
});
mockedList.add("bar");
mockedList.add("baz");
}
#Test
public void iterateOverMockedList() {
for (String each : mockedList) {
System.out.println(each);
}
}

As #Adam says: "Iterating over a list using for-each syntax calls Collection.iterator() under the hood. This returns null because you've not setup mockito to return anything else."
So you have to setup mockito in this way;
#Test
public void test_mergeShipments_increasesByOneWhenAShipmentOfOneAddedToAShipmentORderSizeOfTwo(){
//GIVEN
//Mock the iterator
Iterator<Order> stockIteratorMock = mock(Iterator.class);
//WHEN
//In setUp method you put two objs
when(mockShipmentOrder.size()).thenReturn(2);
//Set a mock for iterator
when(mockShipmentOrder.iterator()).thenReturn(iteratorMock);
// Instruct the iteratorMock when stop to return item
when(iteratorMock.hasNext())
.thenReturn(true)
.thenReturn(true)
.thenReturn(false);
// Instruct the iteratorMock what obj return on each call
// You can skip this: mockShipmentOrders.add(mockOrder1);
when(stockIteratorMock.next())
.thenReturn(mockOrder1)
.thenReturn(mockOrder2);
shipment.mergeShipments(shipment);
//THEN
assertEquals(2, shipment.getShipmentOrders().size());
}
This way is verbose, but you are free to modify the behaviour of the array list and also understand how it works under the wood.

You can't add values to the Mocked element. you can remove #Mock from the list of data and use new keyword to initilize it.
private Shipment shipment;
private Shipment shipment2;
#Mock
private Order mockOrder1;
#Mock
private Order mockOrder2;
#Mock
private Order mockOrder3;
private ArrayList<Order> mockShipmentOrders;
private ArrayList<Order> mockShipmentOrders2;
#Before
public void setUp(){
MockitoAnnotations.initMocks(this);
mockShipmentOrders = new ArrayList<>();
mockShipmentOrders2 = new ArrayList<>();
mockShipmentOrders.add(mockOrder1);
mockShipmentOrders.add(mockOrder2);
mockShipmentOrders2.add(mockOrder3);
shipment = new Shipment(1, mockShipmentOrders);
shipment2 = new Shipment(2, mockShipmentOrders2);
}
#Test
public void test_mergeShipments_increasesByOneWhenAShipmentOfOneAddedToAShipmentORderSizeOfTwo(){
System.out.println(shipment);
System.out.println(shipment2);
shipment.mergeShipments(shipment2);
assertEquals(3, shipment.getShipmentOrders().size());
}

Related

Mock method return type in java

Below is main code consist of one util class and service class using it
#PropertySource("classpath:atlas-application.properties")
public class ApacheAtlasUtils {
#Value("${atlas.rest.address}")
private String atlasURL;
#Value("${atlas.rest.user}")
private String atlasUsername;
#Value("${atlas.rest.password}")
private String atlasPassword;
private AtlasClientV2 client;
public AtlasClientV2 createClient() {
if (client == null) {
return new AtlasClientV2(new String[] {atlasURL}, new String[] {atlasUsername, atlasPassword});
} else {
return client;
}
}
}
Service Class is below :-
#Override
public Page<SearchResultDto> findFilesWithPages(QueryParent queryParent, Pageable pageable)
throws AtlasServiceException {
// Some code
client = new ApacheAtlasUtils().createClient();
//some code
}
I am writing unit test for service method and I am getting exception for createClient method asking for values for url, username and password which should not happen as this should be mocked but the mocking is giving me below error
java.lang.IllegalArgumentException: Base URL cannot be null or empty.
at com.google.common.base.Preconditions.checkArgument(Preconditions.java:141)
at org.apache.atlas.AtlasServerEnsemble.<init>(AtlasServerEnsemble.java:35)
at org.apache.atlas.AtlasBaseClient.determineActiveServiceURL(AtlasBaseClient.java:318)
at org.apache.atlas.AtlasBaseClient.initializeState(AtlasBaseClient.java:460)
at org.apache.atlas.AtlasBaseClient.initializeState(AtlasBaseClient.java:448)
at org.apache.atlas.AtlasBaseClient.<init>(AtlasBaseClient.java:132)
at org.apache.atlas.AtlasClientV2.<init>(AtlasClientV2.java:82)
at com.jlr.stratus.commons.utils.ApacheAtlasUtils.createClient(ApacheAtlasUtils.java:40)
at com.jlr.stratus.rest.service.impl.FileSearchService.findFilesWithPages(FileSearchService.java:49)
The Test code is as follows:-
private FileSearchService fileSearchService;
#Spy
private ApacheAtlasUtils apacheAtlasUtils;
#Mock
private AtlasClientV2 client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
fileSearchService = new FileSearchService();
}
#Test
public void findFilesWithPages_searchAll() throws AtlasServiceException {
Mockito.doReturn(client).when(apacheAtlasUtils).createClient();
service.search(queryParent,pageable);
}
Your idea with spying is adequate (you can even go for mocking if you do not actually need any true implementation of that class).
The problem lies in the implementation:
// Some code
client = new ApacheAtlasUtils().createClient();
//some code
}
Instead of having the ApacheAtlasUtils as an instance variable (or a supplier method) you create the instance on the fly.
Mockito is not smart enough to catch that operation and replace the real object with you spy.
With the supplier method you can set up your test as follows:
#Spy
private FileSearchService fileSearchService = new FileSearchService();
#Spy
private ApacheAtlasUtils apacheAtlasUtils = new ApacheAtlasUtils();
#Mock
private AtlasClientV2 client;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
doReturn(apacheAtlasUtils).when(fileSearchService).getApacheUtils();
}
in your SUT:
#Override
public Page<SearchResultDto> findFilesWithPages(QueryParent queryParent, Pageable pageable)
throws AtlasServiceException {
// Some code
client = getApacheUtils().createClient();
//some code
}
ApacheAtlasUtils getApacheUtils(){
return new ApacheAtlasUtils();
}

Junit Mokito Always empty values are returned

I want to return the value of DAO using mokito but it is not working properly.
It doesn't always work.
As you can see from the code below, it sometimes returns the value I want, but sometimes it doesn't know why.
Below is an example of my code.
Example classes
public Myclass {
#Resource(name=userDao)
UserDao userDao;
#Resource(name=infoDao)
InfoDao infoDao;
public boolean checkUserService(UserVo userVo){
int checkCnt = userDao.getUserCnt(userVo);
InfoVo infoVo = new InfoVo();
infoVo.setUserId(...);
infoVo.set....
....
Map<String,Object> userInfo = infoDao.getInfo(infoVo);
return true or false;
}
}
public UserDao {
public <T> int getUserCnt(T value){
}
}
public InfoDao {
public <T> Map<String,Object> getInfo(T vo){
...
}
}
Test Case
#ExtendWith(MockitoExtension.class)
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
public MyclassTest{
#Mock
UserDao userDao ;
#Mock
InfoDao infoDao ;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
#Test
public testCheckUserService()throws Exception{
UserVo userVo = new UserVo();
when(userDao.getUserCnt(userVo)).thenReturn(1); // This code works normally.
Map<String,Object> checkInfoMap = new HashMap<>();
checkInfoMap.put("name","abc");
InfoVo infoVo = new InfoVo();
//I expected the result to be checkInfoMap, but the empty value will be returned.
when(infoDao.getInfo(infoVo)).thenReturn(checkInfoMap);
//This code likewise always returns an empty value.
when(infoDao.getInfo(anything())).thenReturn(checkInfoMap);
//This code likewise always returns an empty value.
when(infoDao.getInfo(any(InfoVo.class))).thenReturn(checkInfoMap);
}
}
when(userDao.getUserCnt(userVo)).thenReturn(1); This code works, but why doesn't the code below it work?

Inject private field to tested method

Simple question, I have a class with have a List field with Items, I have a method operating on that List, checking if there is a Item with a name given in parameter and returning true or false. I would like to test that method, creating an ArrayList in test class, mocking JdbcItemDao class, and calling a method. Method in test cannot reach an array, how to fix it? Code:
public class JdbcItemDao {
private List<Item> tempStockList;
public JdbcItemDao() {
this.tempStockList = getAllStockItems();
//getting items from my sql, returning them as ArrayList
}
public boolean checkStockItems(String itemName) {
for (Item item : tempStockList) {
if (item.getItemName().equalsIgnoreCase(itemName)) {
return true;
}
}
return false;
}}
Test class:
public class JdbcTest {
JdbcItemDao jdbcItemDao;
List<Item> tempStockList;
#Before
public void setup() {
jdbcItemDao = mock(JdbcItemDao.class);
tempStockList = new ArrayList<>();
tempStockList.add(new Item(1, "LEDTv", new BigInteger("40"),
new Integer("3"), new BigInteger("70")));
}
#Test
public void checkStockItemsName() throws Exception {
assertTrue(jdbcItemDao.checkStockItems("LEDTv"));
}}
If you cannot refactor i guess your left with creating a private class, overriding the method and using that class for testing:
public class JdbcTest {
List<Item> tempStockList = new ArrayList<>();
#Before
public void setup() {
tempStockList = new ArrayList<>();
tempStockList.add(new Item(1, "LEDTv", new BigInteger("40"),
new Integer("3"), new BigInteger("70")));
}
#Test
public void checkStockItemsName() throws Exception {
JdbcItemDao jdbcItemDao = new CustomJdbcItemDao();
assertTrue(jdbcItemDao.checkStockItems("LEDTv"));
}
private class CustomJdbcItemDao extends JdbcItemDao{
#Override
public List<Item> getAllStockItems(){
return tempStockList;
}
}
}
You use the wrong kind of test double. You need a spy (which is a wrapper around the real implementation) instead of a mock (which is a subclass overriding all methods of the mocked class):
jdbcItemDao = spy(new JdbcItemDao());
tempStockList = new ArrayList<>();
tempStockList.add(new Item(1, "LEDTv", new BigInteger("40"),
new Integer("3"), new BigInteger("70")));
doReturn(tempStockList).when(jdbcItemDao).getAllStockItems();
Off cause this only works if you move
this.tempStockList = getAllStockItems();
out of the constructor...
There is a method 'getAllStockItems', it is in JdbcItemDao.java class
public List<Item> getAllStockItems() {
return jdbcTemplate.query(SELECT_ALL_STOCK_ITEMS, itemRowMapper);
}
It is getting a data from MySQL database, so that's why i would like to use fake Array in tests.

Mockito mock java function with inner method

I have a class like so:
#Component
public class AddressConverter {
public Function<Address, AddressDTO> convertToDTO = new Function<Address, AddressDTO>() {
public AddressDTO apply(Address address) {
AddressDTO dto = new AddressDTO();
dto.setAddress1(address.getAddress1());
dto.setAddress2(address.getAddress2());
dto.setCity(address.getCity());
dto.setState(address.getState());
dto.setZip(address.getZip());
dto.setZip4(address.getZip4());
return dto;
}
};
}
I have another class that uses this like so:
#Component
public class ProfileConverter {
#Autowired
private AddressConverter addressConverter;
public Function<Profile, ProfileDTO> convertToDTO = new Function<Profile, ProfileDTO>() {
public ProfileDTO apply(Profile profile) {
ProfileDTO dto = new ProfileDTO();
dto.setEmployeeAddress(addressConverter.convertToDTO.apply(profile.getEmployeeAddress()));
return dto;
}
};
}
I am trying to mock the addressConverter class like:
EDIT: HERE IS THE TEST CLASS
public class ProfileConverterTest {
ProfileConverter converter;
AddressConverter addressConverter;
Profile profile;
ProfileDTO dto;
Address address;
AddressDTO addressDTO;
#Before
public void setUp() {
converter = new ProfileConverter();
addressConverter = Mockito.mock(AddressConverter.class);
profile = new Profile();
profile.setProfileId(123L);
dto = new ProfileDTO();
Mockito.when(addressConverter.convertFromDTO.apply(addressDTO)).thenReturn(address);
Mockito.when(addressConverter.convertToDTO.apply(address)).thenReturn(addressDTO);
ReflectionTestUtils.setField(converter, "addressConverter", addressConverter);
address = new Address("1","2","3","4","5","6");
address.setAddressId(123L);
addressDTO = new AddressDTO("hash","1","2","3","4","5","6");
}
#Test
public void applyReturnsProfileDTO() throws Exception {
ProfileDTO result = converter.convertToDTO.apply(profile);
assertEquals(result.getEmployeeAddress().getAddress1(), profile.getEmployeeAddress().getAddress1());
}
}
I keep getting a NullPointerException in my test on the first Mockito.when line. I am thinking it is caused by the mock addressConverter class calling a function calling an inner method. The function might be null?
You are expecting Mockito to call your class constructor which will initialize the field convertToDTO: this is not the case, and the simple test here demonstrate it:
#Test
public void test_that_yeepee_works() {
final Yeepee y = Mockito.mock(Yeepee.class);
Assertions.assertNotNull(y.myObject); // fails.
}
public static class Yeepee {
public Object myObject = new Object();
}
You might try Mockito.spy instead.
Or you need to explicitly do it:
converter = new ProfileConverter();
addressConverter = Mockito.mock(AddressConverter.class);
addressConverter.convertToDTO = (Function<Address, AddressDTO>) Mockito.mock(Function.class);
If you were using getters, Mockito might have done it for you using smart mocks.
I think, NPE is because you have not created a mock for AddressConverter class. You should write something like below:
AddressConverter addressConverter = Mockito.mock(AddressConverter.class);
Mockito.when(addressConverter.apply(any(Address.class))).thenReturn(addressDTO);
Note: any (Address.class) will match any object of Address class. If you want to test weather your address object is being used while calling (mocking) apply method, go ahead and add hashcode() and equals() implementation in your Address class.

org.mockito.exceptions.misusing.MissingMethodInvocationException

I am getting following exceptions when I run Junit test.
org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);
Also, this error might show up because:
you stub either of: final/private/equals()/hashCode() methods. Those methods cannot be stubbed/verified.
inside when() you don't call method on mock but on some other object.
the parent of the mocked class is not public. It is a limitation of the mock engine.
Following is my code and the exception was thrown at the second when statement. I don't think that my test code didn't violate what the exception claims. I spent for a while, but couldn't figure out. Could someone help? What I need to test is getPermProducts method. In the getPermProducts method, I want to ignore isTempProduct method. So, when p1 came, I want it returns false, and when p2 came, I want it returns true etc..
#Named(ProductManager.NAME)
public class ProductManager {
#Resource(name = ProductService.NAME)
private ProductService productService;
public List<Product> getPermProducts(Set<Product> products) {
Iterator<Product> it = products.iterator();
List<Product> cProducts = new ArrayList<Product>();
Product p;
while (it.hasNext()) {
p = it.next();
if (!isTempProduct(p)) {
cProducts.add(p);
}
}
return cProducts;
}
public Boolean isTempProduct(Product product) {
if (product instanceof PermProduct) {
return false;
}
Set<ProductItems> pItems = product.getProductItems();
if (pItems.isEmpty()) {
return false;
}
Iterator<ProductItem> itr = pItems.iterator();
while (itr.hasNext()) {
if (itr.next() instanceof TempItem) {
return true;
}
}
return false;
}
public Product getProduct(Integer productId) {
Product p = productService.getProduct(productId);
return p;
}
}
#RunWith(MockitoJUnitRunner.class)
public class ProductManagerTest {
#InjectMocks
private ProductManager mockProductManager;
#Mock
private ProductService mockProductService;//not being used here
private static final Integer PRODUCT_ID_1 = 1;
private static final Integer PRODUCT_ID_2 = 2;
#Test
public void getProduct(){
Product p1 = mock(PermProduct.class);
p1.setProductId(PRODUCT_ID_1);
when(mockProductManager.getProductId()).thenReturn(PRODUCT_ID_1);
when(mockProductManager.isTempProduct(p1)).thenReturn(false);
Product p2 = mock(TempProduct.class);
p2.setProductId(PRODUCT_ID_2);
when(mockProductManager.isTempProduct(p2)).thenReturn(true);
List<Product> products = Mock(List.class);
products.add(p1);
products.add(p2);
Iterator<Product> pIterator = mock(Iterator.class);
when(prodcuts.iterator()).thenReturn(pIterator);
when(pIterator.hasNext()).thenReturn(true, true, false);
when(pIterator.next()).thenReturn(p1, p2);
asserEquals(1, mockProductManager.getPermProducts(products).size());
}
}
SOLUTION: I updated my test based on enterbios's answer. I used partial mocking, but as enterbios suggested, we should avoid it, but sometimes we need it. I found that we can have both non-mocked and partial mocked class same time.
#RunWith(MockitoJUnitRunner.class)
public class ProductManagerTest {
#InjectMocks
private ProductManager productManager;//not being used here
#Mock
private ProductService mockProductService;//not being used here
#Spy
private OtherService other = new OtherService();//not being used here
#InjectMocks
final ProductManager partiallyMockedProductManager = spy(new ProductManager());
private static final Integer PRODUCT_ID_1 = 1;
private static final Integer PRODUCT_ID_2 = 2;
#Test
public void getProduct() {
Product p1 = new PermProduct();
p1.setProductId(PRODUCT_ID_1);
Product p2 = new Product();
p2.setProductId(PRODUCT_ID_2);
List<Product> products = new ArrayList<Product>();
products.add(p1);
products.add(p2);
doReturn(false).when(partiallyMockedProductManager).isTempProduct(p1);
doReturn(true).when(partiallyMockedProductManager).isTempProduct(p2);
assertEquals(1, partiallyMockedProductManager.getPermProducts(products).size());
verify(partiallyMockedProductManager).isTempProduct(p1);
verify(partiallyMockedProductManager).isTempProduct(p2);
}
}
Your mockProductManager is actually not a mock but an instance of ProductManager class. You shouldn't mock a tested object. The way to mock some methods from tested object is using a spy but I recommend you to not use them, or even to not think about them unless you're fighting with some ugly legacy code.
I think your second 'when' should be replaced with assertion, like:
assertFalse(mockProductManager.isTempProduct(p1));
because what you really want to check in this test is if productManager.isTempProduct(p1) returns false for all instances of PermProduct. To verify results of some method calls/objects states you should use assertions. To make your life with assertions easier you can take a look on some helpful libraries like Hamcrest or FEST (http://docs.codehaus.org/display/FEST/Fluent+Assertions+Module). FEST is simpler for beginners I think.

Categories