2013-08-13 15 views
5

Oto moje wymagania dla testów jednostkowych:Unit Testing - tylko realizacji wynosi ułatwić testowanie

  1. Chciałbym jednostki przetestować moje zajęcia produkcyjne
  2. Chciałbym oddzielić kod testowy i kod produkcyjny oprócz takich że mogę zwolnić kod produkcyjny tylko

To wydaje się rozsądne wymagania. Jednak zawsze pojawia się problem, gdy trzeba użyć metod takich jak assertEquals dla obiektów, ponieważ wymagają one nadpisania metody equals. Metoda equals musiałaby zostać zaimplementowana w klasach produkcyjnych , ale w rzeczywistości jest używana tylko do testowania. Staje się to jeszcze gorsze, gdy dobre praktyki kodowania nakazują, że jeśli equals zostanie przesłonięte, to należy również zaimplementować hashCode, powodując jeszcze więcej nieużywanego kodu produkcyjnego, który zaśmierza klasy produkcyjne.

Oto prosty przykład z User modelu (IntelliJ autoimplemented equals i hashCode)

public class User 
{ 
    public long id; 
    public long companyId; 
    public String name; 
    public String email; 
    public long version; 

    @Override 
    public boolean equals(Object o) 
    { 
     if(this == o) return true; 
     if(o == null || getClass() != o.getClass()) return false; 
     User user = (User) o; 
     if(companyId != user.companyId) return false; 
     if(id != user.id) return false; 
     if(version != user.version) return false; 
     if(!email.equals(user.email)) return false; 
     if(!name.equals(user.name)) return false; 
     return true; 
    } 

    @Override 
    public int hashCode() 
    { 
     int result = (int) (id^(id >>> 32)); 
     result = 31 * result + (int) (companyId^(companyId >>> 32)); 
     result = 31 * result + name.hashCode(); 
     result = 31 * result + email.hashCode(); 
     result = 31 * result + (int) (version^(version >>> 32)); 
     return result; 
    } 
} 

Jak można zauważyć, equals i hashCode zajmuje dużo miejsca i zaśmiecanie klasy.

Jednym z rozwiązań tego problemu może być stworzenie klasy, UserTester, która mogłaby mieć metodę assertUserEquals, która mogłaby zostać użyta zamiast np. JUnit's assertEquals.

Innym rozwiązaniem może być utworzenie UserComparator. Jednak wydaje się, że JUnit nie ma żadnego assertEquals, który zajmuje Comparator.

Jakie są najlepsze praktyki w tym zakresie?

Odpowiedz

1

Uniutils ma idealną metodę odbijania równą, którą można wykorzystać do testowania jednostkowego. W ten sposób Twój kod produkcyjny pozostaje jasny z wszystkich tych rzeczy testowych.

public class User { 

    private long id; 
    private String first; 
    private String last; 

    public User(long id, String first, String last) { 
     this.id = id; 
     this.first = first; 
     this.last = last; 
    } 
} 

Później w teście:

User user1 = new User(1, "John", "Doe"); 
User user2 = new User(1, "John", "Doe"); 
assertReflectionEquals(user1, user2); 

Jeśli używasz Mockito ma swoje własne środki, aby zrobić to samo:

Mockito.verify(userDeleter).delete(Mockito.refEq(user)); 
+0

Właśnie sprawdziłem ich dokumentację. Wydaje się, że powinieneś użyć 'assertReflectionEquals' zamiast' assertEquals'. Wydaje się, że jest to rozwiązanie mojego problemu. – foens

+0

Mój zły, dzięki za zauważenie – Jk1

+0

Ehh - Znalazłem mały problem, którego nie przedstawiłem na moim pytaniu. A co z używaniem frameworków takich jak [mockito] (https://code.google.com/p/mockito/) do kpiny? Używając metod takich jak 'verify (userDeleter) .delete (user)', używa również 'equals' do testowania argumentów. Czy znasz jakieś rozwiązanie tego problemu? – foens

0

Nie jest to najbardziej efektywny, ale jeden z możliwych sposobów jest porównywanie pól za pomocą refleksji.

public class Foo { 

    int x; 

    Foo(int in){ 
    x = in; 
    } 

    public static void main(String[] args) throws Exception{ 

     Foo o1 = new Foo(1),o2= new Foo(1); 
     boolean allMatch = true; 
     Class<?> c = Class.forName("Foo"); 
     Field[] fields = c.getDeclaredFields(); 

     for(Field f: fields){ 
      allMatch &= f.get(o1)==f.get(o2); 
     } 
    } 
} 
+0

Wygląda na to, że odpowiedź Jk1 to po prostu starannie zapakowana wersja twojego pomysłu. Czy to naprawdę tak wolno? – foens

+0

Odbicie odwiertu ma swoje narzuty, ale zależy to od tego, jak i kiedy go używasz. Myślę, że używanie go podczas fazy testowej nie powinno stanowić problemu, ale znowu to naprawdę zależy od tego, ile połączeń odbiciowych wykonasz. Ponadto lepiej używać istniejącej biblioteki, ale wiedząc, że implementacja może pomóc w dostosowaniu rozwiązania. – rocketboy

0

widzę dwie różne rzeczy:

  1. Czasami to nie jest pożądane, aby zastąpić hashcode & równymi. To może zmienić zachowanie programu i może zranić swoją wydajność, patrz Java Overriding hashCode() method has any Performance issue?

  2. Jeśli nie ma wymogu, aby zastąpić klienta hashcode & równa, jak to obiekt wartość, nie zrobiłby tego.Twój powinien dostarczyć taki kod, który dokładnie spełnia kryteria klienta, a nie więcej. Twój test powinien zajmować się oryginalną implementacją w domyślnym obiekcie.

Powiązane problemy