2015-05-19 4 views
8

W teście jednostkowym chcę sprawdzić, czy dwie listy zawierają te same elementy. Lista do przetestowania jest zbudowana z listy obiektów Person, z których wypakowywane jest jedno pole typu String. Druga lista zawiera literały String.Java 8: Bardziej efektywny sposób porównywania list różnych typów?

Często stwierdza następujący fragment kodu do wykonania tego zadania (patrz this answer):

List<Person> people = getPeopleFromDatabasePseudoMethod(); 
List<String> expectedValues = Arrays.asList("john", "joe", "bill"); 

assertTrue(people.stream().map(person -> person.getName()).collect(Collectors.toList()).containsAll(expectedValues)); 

Klasa Person jest zdefiniowana w:

public class Person { 

    private String name; 
    private int age; 

    public String getName() { 
     return name; 
    } 

    public void setName(final String name) { 
     this.name = name; 
    } 

    // other getters and setters 
} 

w powyższym przykładzie, do wykazu osób (lub ludzie) jest przekształcana na listę Ciągów przy użyciu technik Java 8, a porównanie odbywa się w staromodny sposób.

Teraz zastanawiam się, czy istnieje bardziej bezpośredni lub bardziej efektywny sposób porównania za pomocą innych instrukcji Java 8, na przykład allMatch() lub niektóre Predicate<T> lub coś innego.

+0

Zamiast tego należy użyć dopasowań [Hamcrest] (http://hamcrest.org/) – Makoto

+0

Czy na pewno chcesz sprawdzić 'zawieraAll'? Czy kolejność i rozmiar list mogą być różne? –

+0

@TagirValeev Chcę się upewnić, że osoby "Lista " zawierają wszystkie osoby określone na liście statycznej, porównując ich unikalne nazwy. Kolejność list może być różna, ale dla pomyślnego zapewnienia, wielkość list powinna być taka sama. –

Odpowiedz

13

Kod Twojego pytania nie odzwierciedla tego, co opisujesz w komentarzach. W komentarzach mówisz, że wszystkie nazwiska powinny być obecne, a rozmiar powinien się zgadzać, innymi słowy, tylko kolejność może być inna.

Twój kod jest

List<Person> people = getPeopleFromDatabasePseudoMethod(); 
List<String> expectedValues = Arrays.asList("john", "joe", "bill"); 

assertTrue(people.stream().map(person -> person.getName()) 
       .collect(Collectors.toList()).containsAll(expectedValues)); 

których brakuje test wielkości people, innymi słowy pozwala duplikaty. Ponadto, używając containsAll, łącząc dwie bardzo nieefektywne. Jest o wiele lepiej, jeśli używasz typu kolekcji, która odzwierciedla ty zamiar, czyli nie ma duplikatów, nie dba o porządku i ma sprawną odnośnika:

Set<String> expectedNames=new HashSet<>(expectedValues); 
assertTrue(people.stream().map(Person::getName) 
       .collect(Collectors.toSet()).equals(expectedNames)); 

Dzięki takiemu rozwiązaniu nie trzeba przetestować dla rozmiar ręcznie, jest już dorozumiany, że zestawy mają ten sam rozmiar, jeśli pasują, tylko kolejność może być inna.

Jest to rozwiązanie, które nie wymaga gromadzenia nazwisk persons:

Set<String> expectedNames=new HashSet<>(expectedValues); 
assertTrue(people.stream().allMatch(p->expectedNames.remove(p.getName())) 
      && expectedNames.isEmpty()); 

ale działa tylko wtedy, gdy expectedNames jest tymczasowy zestaw stworzony z statycznego kolekcji oczekiwanych nazwisk. Gdy tylko zdecydujesz się zastąpić kolekcję statyczną przez Set, pierwsze rozwiązanie nie wymaga zestawu tymczasowego, a drugie nie ma nad nim żadnej przewagi.

+0

Przyznaję, że mam również sprawdzanie rozmiarów list, ale nie zapisałem tego w kodzie pytania. I masz rację, używając zestawów zamiast list. Twoje drugie rozwiązanie jest tym, czego oczekiwałem. Ale jak powiedziałeś, nie ma żadnej przewagi nad twoim pierwszym rozwiązaniem, wybieram to. Ale teraz używam 'assertEquals' zamiast' assertTrue'. –

4

Jeżeli liczba elementów musi być taka sama, to byłoby lepiej, aby porównać zestawach:

List<Person> people = getPeopleFromDatabasePseudoMethod(); 
Set<String> expectedValues = new HashSet<>(Arrays.asList("john", "joe", "bill")); 
assertEquals(expectedValues, 
    people.stream().map(Person::getName).collect(Collectors.toSet())); 

equals sposób prawidłowo realizowanych zestawów powinny móc porównać różne rodzaje zestawów: liczy się tylko czy zawartość jest taka sama (ignorując kolejność oczywiście).

Korzystanie z assertEquals jest wygodniejsze, ponieważ w przypadku awarii komunikat o błędzie będzie zawierał ciąg znaków w twoim zestawie.

+0

Tak, używanie zestawów ma absolutnie sens. –