2013-08-13 10 views
5

Hier meine Anforderungen für Unit-Tests sind:Test Unit - Umsetzung equals nur Tests zu erleichtern

  1. Ich mag würde meine Produktion Klassen Einheit testen
  2. Ich mag würde Testcode und Produktionscode trennen auseinander solche dass ich nur Produktionscode freigeben kann

Dies scheint wie angemessene Anforderungen. Ein Problem tritt jedoch immer dann auf, wenn ich Methoden wie assertEquals für Objekte verwenden muss, da diese die Methode equals außer Kraft setzen müssen. Die Methode equals müsste in den Produktionsklassen implementiert werden, wird aber nur zum Testen verwendet. Dies wird noch schlimmer, wenn eine gute Kodierungspraxis vorschreibt, dass, wenn equals überschrieben wird, auch hashCode implementiert werden sollte, was zu noch mehr unbenutztem Produktionscode führt, der die Produktionsklassen überlagert.

Hier ist ein einfaches Beispiel mit einem User Modell (IntelliJ autoimplemented equals und 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; 
    } 
} 

Wie es zu sehen ist, equals und hashCode nimmt die Klasse viel Platz und clutters.

Eine Lösung für das Problem könnte sein, eine Klasse UserTester zu erstellen, die eine assertUserEquals-Methode haben könnte, die anstelle von zB verwendet werden könnte. JUnits assertEquals.

Eine andere Lösung könnte sein, eine UserComparator zu erstellen. Es scheint jedoch nicht so, als hätte JUnit eine assertEquals, die eine Comparator dauert.

Was sind die besten Praktiken in diesem Punkt?

Antwort

1

Uniutils hat eine perfekte Reflexion gleich Methode, die Sie für Komponententests verwenden können. Auf diese Weise bleibt Ihr Produktionscode von all diesen Testdaten klar.

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; 
    } 
} 

Später im Test:

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

Wenn Sie Mockito verwenden es hat seine eigenen Mittel ist, das Gleiche zu tun:

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

Nur ihre Dokumentation überprüft. Es scheint, dass Sie 'assertReflectionEquals' anstelle von' assertEquals' verwenden sollten. Es scheint, dass dies die Lösung für mein Problem ist. – foens

+0

Mein böser, danke fürs merken – Jk1

+0

Ehh - Habe gerade ein kleines Problem gefunden, das ich in meiner Frage nicht vorgestellt habe. Was ist mit der Verwendung von Frameworks wie [Mockito] (https://code.google.com/p/mockito/) zum Spotten? Wenn Sie Methoden wie 'verify (userDeleter) .delete (user)' verwenden, verwendet es auch 'equals' zum Testen der Argumente. Kennen Sie eine Lösung für dieses Problem? – foens

0

nicht die effizienteste, aber eine Möglichkeit, ist es, Felder mit Reflektion zu vergleichen.

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

Es scheint, dass die Antwort von Jk1 einfach eine ordentlich verpackte Version Ihrer Idee ist. Ist es wirklich so langsam? – foens

+0

Nun Reflexion hat seine Gemeinkosten, aber das hängt davon ab, wie und wann Sie es verwenden. Ich denke, dass es während der Testphase kein Problem sein sollte, aber dann hängt es wirklich davon ab, wie viele reflektierende Aufrufe du machst. Außerdem ist es besser, eine vorhandene Bibliothek zu verwenden, aber wenn Sie die Implementierung kennen, können Sie Ihre Lösung anpassen. – rocketboy

0

Ich sehe zwei verschiedene Dinge:

  1. manchmal wünschenswert, es ist nicht hashcode & gleich außer Kraft zu setzen. Es kann das Verhalten Ihres Programms ändern und es kann Ihre Leistung schaden, Java Overriding hashCode() method has any Performance issue?

  2. sehen Wenn es keine Kundenanforderung ist außer Kraft zu setzen hashcode & gleich, wie es Wertobjekt ist, würden Sie nicht tun.Sie sollten einen solchen Code bereitstellen, der genau die Kundenkriterien erfüllt, nicht mehr. Ihr Test sollte sich mit der ursprünglichen Implementierung im Standardobjekt befassen.