Home > Software engineering >  How can I get two method calls on the same Mock to return different values?
How can I get two method calls on the same Mock to return different values?

Time:10-22

I have a function that I am trying to mock that contains recursive logic in the form of a while loop and i'm trying to figure out how to access the inner part of the while loop without looping forever.

//this is supposed to go through all the items in the grocery list given the parameters until the groceries are all checked out 
public void checkOut(String maxItems, String code){
    List<cereal> groceryList;
    groceryList = groceryListDao.getList(String maxItems, String code);
    while (!groceryList.isEmpty()){
    groceryListDao.total();
    //other logic
    groceryList = groceryListDao.getList(String maxItems, String code);
    }
}

I am able to write a junit test to verify that the while loop is never entered if the grocery list is empty. However, i'm not sure how to write code to test that the while loop is entered because I need to mock groceryListDao.getList to not be empty to enter the while loop and then empty to exit the while loop. I don't know how to do both.

@Test
public void checkout() {
    List<Cereal> cereal = new ArrayList<>();
    Cereal x = new Cereal();
    cereal.add(x);
    when(groceryListDao.getList(anyString(), anyString())).thenReturn(cereal);
    groceryService.checkout("10", "A5ALV350IIXL");
    verify(groceryListDao, times(1).total());
}

How do I verify that total() is being called inside the loop without getting stuck?

CodePudding user response:

You can chain thenReturn, so that subsequent calls to the mock return different things:

public class GroceryServiceTest {
    @Test
    public void checkout() {
        GroceryService.GroceryListDao groceryListDao = mock(GroceryService.GroceryListDao.class);
        GroceryService groceryService = new GroceryService(groceryListDao);
        List<GroceryService.Cereal> cereal = new ArrayList<>();
        GroceryService.Cereal x = new GroceryService.Cereal();
        cereal.add(x);
// first return a list with one item, then an empty list
        when(groceryListDao.getList(anyString(), anyString())).thenReturn(cereal).thenReturn(Collections.emptyList());
        groceryService.checkout("10", "A5ALV350IIXL");
        verify(groceryListDao, times(1)).total();
    }
}

This is not a perfect test, as the mock would return an empty list without an intervening call to total.

You can simulate the semantics of your DAO like this:

public class GroceryServiceTest {
    @Test
    public void checkout() {
        GroceryService.GroceryListDao groceryListDao = mock(GroceryService.GroceryListDao.class);
        GroceryService groceryService = new GroceryService(groceryListDao);
        List<GroceryService.Cereal> cereal = new ArrayList<>();
        AtomicBoolean totalCalled = new AtomicBoolean(false);
        GroceryService.Cereal x = new GroceryService.Cereal();
        cereal.add(x);
        when(groceryListDao.getList(anyString(), anyString())).thenAnswer(new Answer<List<GroceryService.Cereal>>() {

            @Override
            public List<GroceryService.Cereal> answer(InvocationOnMock invocationOnMock) throws Throwable {
                if (totalCalled.get()) {
                    return Collections.emptyList();
                } else {
                    return cereal;
                }
            }
        });
        doAnswer(new Answer<Void>() {
            @Override
            public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
                totalCalled.set(true);
                return null;
            }
        }).when(groceryListDao).total();
        groceryService.checkout("10", "A5ALV350IIXL");
        verify(groceryListDao, times(1)).total();
    }
}

For completeness, here's the GroceryService code:

import java.util.List;

public class GroceryService {
    final GroceryService.GroceryListDao groceryListDao;

    public GroceryService(GroceryService.GroceryListDao groceryListDao) {
        this.groceryListDao = groceryListDao;
    }
    interface GroceryListDao {
        List<Cereal> getList(String maxItems, String code);
        void total();
    }
    static class Cereal {}
    public void checkout(String maxItems, String code){
        List<Cereal> groceryList;
        groceryList = groceryListDao.getList(maxItems, code);
        while (!groceryList.isEmpty()){
            groceryListDao.total();
            //other logic
            groceryList = groceryListDao.getList(maxItems, code);
        }
    }
}
  • Related