Search code examples
javajunitmockitojunit5assertj

How do I test the API behavior when mock returns unordered collection Map?


I have an API, API calls a service which returns Map<String, List<String>>, API converts it to List and returns it.

public List<EmployeeWrapper> getEmployees(int id) {
    Map<Integer, List<String>> employees = employeeRepository.getEmployees(id);
    return employees.entrySet().stream()
            .map(entry -> new EmployeeWrapper(entry.getKey(), entry.getValue()))
            .collect(Collectors.toList());
}

Now, I need to write a test case to cover this behavior, but the problem here is Mock returns HashMap and the order of List<EmployeeWrapper> could be different on each invocation.

@Test
void testEmployeeService() {
    // when
    EmployeeRepository employeeRepository = Mockito.mock(EmployeeRepository.class);
 
    Map<Integer, List<String>> employeeIdsToEmployee = 
            Map.of(1, List.of("HR", "Account"), 2, List.of("Marketing"));
    Mockito.when(employeeRepository.getEmployees(Mockito.any())).thenReturn(employeeIdsToEmployee);
    
    // call service
    List<EmployeeWrapper> employees = employeeService.getEmployees(10);
    
    // assert
    assertThat(employees).isNotNull().hasSize(2);
    EmployeeWrapper employeeWrapper1 = employees.get(0);
    EmployeeWrapper employeeWrapper2 = employees.get(1);

    assertThat(employeeWrapper1).isNotNull().extracting("id", "departments")
            .containsExactly(1, List.of("HR", "Account"));
    assertThat(employeeWrapper2).isNotNull().extracting("id", "departments")
            .containsExactly(2, List.of("Marketing"));
}

The problem here is employees.get(0) and employees.get(1) wouldn't be always the same object as the employee List is built using an unordered collection.

is there a better way to test if the behavior of an API is dependent on unordered collection?


Solution

  • AssertJ's assertions for iterables has a 'containsExactlyInAnyOrder' method.

    You have to start the assertion on the list:

    assertThat(employees)
             .extracting("id", "departments")
             .containsExactlyInAnyOrder(
                tuple(1, List.of("HR", "Account")),
                tuple(2, List.of("Marketing")));
    

    BTW, the extracting method also supports lambda's, so it can be:

    assertThat(employees)
             .extracting(
                  EmployeeWrapper::getId,
                  EmployeeWrapper::getDepartments)
             .containsExactlyInAnyOrder(
                  tuple(1, List.of("HR", "Account")),
                  tuple(2, List.of("Marketing")));