Search code examples
javajunitstream

How to compare two lists in stream


I want to compare (assert equals) for training purposes two lists in stream. So every id from sampleUsers() with every id from body of response entity should be compared. Should I create another stream? If yes then how to use it?

@Test
  void findAllUsers() {
    when(userService.findAll()).thenReturn(sampleUsers());
    ResponseEntity<List<UserDto>> responseEntity = userController.findAll();

    //first assert
    assertEquals(responseEntity.getStatusCode().value(), 200);

    //second assert
    assertEquals(responseEntity.getBody().size(), sampleUsers().size());

    //third assert
    responseEntity.getBody().stream()
          .filter(user -> user.getId() != null)
          .filter(user -> user.getUsername() != null)
          //TODO check for each user if: 
          //     user.getId is equal to id from sampleUsers() list
          //     something like: assert equals user.getId(), sampleUsers.get(0).getId()
    }

Solution

  • There are many ways to do that assertion. You could override equals() in UserDto class.

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || this.getClass() != o.getClass()) return false;
        UserDto userDto = (UserDto) o;
        return this.id.equals(userDto.id);
    }
    

    And implement a Comparator for UserDto class. This is going to be used to sort both lists.

    public class UserDtoComparatorById implements Comparator<UserDto> {
        @Override
        public int compare(UserDto o1, UserDto o2) {
            return o1.getId().compareTo(o2.getId());
        }
    }
    

    Finally.

    @Test
    void findAllUsers() {
        when(userService.findAll()).thenReturn(sampleUsers());
        ResponseEntity<List<UserDto>> responseEntity = userController.findAll();
    
        //first assert
        assertEquals(responseEntity.getStatusCode().value(), 200);
    
        //second assert (this is optional)
        assertEquals(responseEntity.getBody().size(), sampleUsers().size());
       
        // to prevent false negatives, in case both lists have same objects
        // but different orders 
        Comparator<UserDto> comparator = new UserDtoComparatorById();
        sampleUsers.sort(comparator);
        responseEntity.getBody().sort(comparator);
    
        //third assert
        assertEquals(responseEntity.getBody(), sampleUsers);
    
        //same assertion but using streams
        AtomicInteger i = new AtomicInteger(0);
        assertEquals(
            list1.stream()
                 .filter(user -> user.equals(list2.get(i.getAndIncrement())))
                 // or: filter(user -> user.getId().equals(list2.get(i.getAndIncrement()).getId()))
                 .count(), 
            list1.size()
        );
    }
    

    The third assert will work because both lists are sorted and assertEquals() will invoke UserDto.equals() for each element in the same position. As UserDto.equals() is overridden to check only UserDto.id, it will check the id instead of the Object pointer (address in memory), therefore returning true.

    Note that in the second alternative you can not use int i because variables inside lambda functions need to be final. A simple workaround is using AtomicInteger.