How to write Junit test for Cosmos container query items?

391 Views Asked by At

I have a requirement where I need to write Junit tests for the following piece of code. The issue I am unable to figure out how to create an actual dummy instance of Iterable<FeedResponse<T>> because if I pass mocked instance of Iterable<FeedResponse<T>>,then the for loop in the next step throws null pointer exception as it tries to iterate over the mocked instance and finds nothing in it. Given below are the block of codes from the actual class and test method. Any lead would be highly appreciated.

Actual class - for loop is the issue

Iterable<FeedResponse<Employee>> feedResponseIterator = cosmosContainer
                        .queryItems(query, queryOptions, Employee.class)
                        .iterableByPage(continuationToken, pageSize);
for (FeedResponse<Employee> page : feedResponseIterator) {
                    } 

Test Method - This approach throws null pointer from the for loop as discussed above

@Mock
CosmosClientManager cosmosClientManager;

@Mock
CosmosContainer cosmosContainer;

@Mock
CosmosPagedIterable<Employee> page;
    
@Mock
Iterable<FeedResponse<Employee>> feedResponseIterator;

when(cosmosClientManager.getCosmosContainer(anyString())).thenReturn(cosmosContainer);
when(cosmosContainer.queryItems(anyString(), any(), eq(Employee.class))).thenReturn(page);
when(page.iterableByPage(any(), anyInt())).thenReturn(feedResponseIterator);
1

There are 1 best solutions below

2
knittl On

Your iterable doesn't stub any of its methods, meaning that calling iterator() on it (which is what the enhanced for loop does) will get you a null. Either stub all required calls of the mock (iterator() and then return a proper iterator or stub all important methods on the iterator …), or change your code to return an actual CosmosPagedIterable instance.

Note that your field is called feedResponseIterator, but it is not an iterator but an Iterable (an interface on clases which can return an iterator)

Here's an example how you could use a real instance:

@Mock
CosmosClientManager cosmosClientManager;

@Mock
CosmosContainer cosmosContainer;

@Mock
CosmosPagedIterable<Employee> page;

when(cosmosClientManager.getCosmosContainer(anyString())).thenReturn(cosmosContainer);
when(cosmosContainer.queryItems(anyString(), any(), eq(Employee.class))).thenReturn(page);

// define responses:
final List<FeedResponse<Employee>> = List.of(new FeedResponse<>(...), ...);
// the iterable returns an iterator for the predefined list:
final Iterable<FeedResponse<Employee>> iterable = responses::iterator; 
when(page.iterableByPage(any(), anyInt())).thenReturn(iterable);

If possible, try to push the real behavior further up in the call chain. Have your container return a real CosmosPagedIterable instance for the page.

This related questions explains why it is problematic to mock/stub collections/containers: Why can non-empty mocked list not be looped?