Search code examples
javamockitojunit5

How to mock adding to a List<Object> inside an Iterator


How do I mock adding to a list inside iterator like the code below? I want to add to the list because my method returns List and I want to do an assert on that but not sure how to add my object to the list inside the iterator? Or am I misunderstanding and the mocking should occur in the Util.jsonToObject?

public List<Users> getUsers(String userId) {
    List<Users> usersList = new ArrayList<>();
    Table table = dynamoDB.getTable("users");
    ItemCollection<QueryOutcome> items = table.getIndex("userIndex").query(QuerySpec);
    Iterator<Item> iterator = items.iterator();
    while (iterator.hasNext()) {
        list.add(Util.jsonToObject(iterator.next().toJSONPretty(), Users.class));
    }
    return usersList;
}

Here is the test

    List<Users> mockList = mock(ArrayList.class);
    Users details = new Users("1", UUID.randomUUID().toString(), null, "");
    List<Users> usersList = new ArrayList<>();
    usersList.add(details);
    Table mockTable = mock(Table.class);
    Item item = mock(Item.class);
    mockTable.putItem(item);
    Index mockIndex = mock(Index.class);
    ItemCollection<QueryOutcome> mockCollection = mock(ItemCollection.class);
    Item mockItem = mock(Item.class);
    IteratorSupport<Item, QueryOutcome> mockIterator = mock(IteratorSupport.class);
    when(dynamoDB.getTable("users")).thenReturn(mockTable);
    when(mockTable.getIndex(anyString())).thenReturn(mockIndex);
    when(mockIndex.query(any(QuerySpec.class))).thenReturn(mockCollection);
    when(mockCollection.iterator()).thenReturn(mockIterator);
    when(mockIterator.hasNext()).thenReturn(true).thenReturn(false);
    doReturn(true).when(mockList).add(details);
    when(mockIterator.next()).thenReturn(mockItem);
    when(mockItem.toJSONPretty()).thenReturn("");
    assertSame(dynamoDBService.getUsers("").get(0), usersList);

Solution

  • Unsolicited Suggestion 1: Learn how to use the Mockito annotations, they make everything easier to do and easier to read.

    Unsolicited Suggestion 2: Use doReturn().when() instead of when().thenReturn(). Try a google search for "is doReturn better than thenReturn" for discussion.

    Suggested Answer: Don't instantiate a new User class mock object in the subbing of the list.add method. Instead, match any instance of the User class.

    Here is some example code:

    @ExtendWith(MockitoExtension.clss)
    public class TestBlam
    {
        private static final String VALUE_PRETTY_JSON = "{}";
        
        private Blam classToTest; // Blam is the class that you are actually testing.
        
        @Mock
        private ItemCollection<QueryOutcome> mockCollection;
        
        @Mock
        private Item mockItem;
        
        @Mock
        private IteratorSupport<Item, QueryOutcome> mockIterator;
        
        @BeforeEach
        void beforeEach()
        {
            // instantiate the thing that you will actually test.
            classToTest = new Blam();
        }
        
        @Test
        void blamTest()
        {
            doReturn(mockIterator).when(mockCollection).iterator();
            doReturn(true).doReturn(false).when(Iterator).hasNext();
    
    // The answer to your question is the next line.
            doReturn(true).when(mockList).add(any(User.class));
    
    
            doReturn(mockItem).when(mockIterator).next();
            doReturn(VALUE_PRETTY_JSON).when(mockItem).toJSONPretty();
            
            // perform the test. ...
        }
    }