2013-08-13 9 views
5

Qui sono i miei requisiti per il test delle unità:Unit Testing - attuazione uguali solo per facilitare la prova

  1. Vorrei unità di testare le mie classi di produzione
  2. vorrei separare il codice di prova e il codice di produzione a parte, quali che posso rilasciare solo il codice di produzione

Questo sembra requisiti ragionevoli. Tuttavia, si verifica sempre un problema quando ho bisogno di utilizzare metodi come assertEquals sugli oggetti in quanto ciò richiede che il metodo equals sia sovrascritto. Il metodo equals dovrebbe essere implementato nelle classi di produzione ma in realtà viene utilizzato solo per il test. Questo diventa ancora più grave quando le buone pratiche di codifica impongono che se si sostituisce equals, allora anche hashCode dovrebbe essere implementato con conseguente codice di produzione ancora più inutilizzato che ingombra le classi di produzione.

Ecco un semplice esempio con un modello di User (IntelliJ autoimplemented equals e 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; 
    } 
} 

Come si può vedere, equals e hashCode occupa un sacco di spazio e ingombra la classe.

Una soluzione al problema potrebbe essere la creazione di una classe, UserTester, che potrebbe avere un metodo assertUserEquals che potrebbe essere utilizzato al posto di es. JUnit's assertEquals.

Un'altra soluzione potrebbe essere quella di creare un UserComparator. Tuttavia, non sembra che JUnit abbia uno assertEquals che prende uno Comparator.

Quali sono le migliori pratiche su questo punto?

risposta

1

Uniutils ha un riflesso perfetto uguale al metodo che è possibile utilizzare per il test dell'unità. In questo modo il tuo codice di produzione rimane chiaro da tutto questo materiale di prova.

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

Più tardi nella prova:

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

Se stai usando Mockito che ha il proprio modo di fare la stessa cosa:

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

Basta controllare la loro documentazione. Sembra che tu debba usare "assertReflectionEquals" invece di "assertEquals". Sembra che questa sia la soluzione al mio problema. – foens

+0

Mio male, grazie per aver notato – Jk1

+0

Ehh - Ho appena trovato un piccolo problema che non ho presentato nella mia domanda. Che dire dell'utilizzo di framework come [mockito] (https://code.google.com/p/mockito/) per il mocking? Quando si usano metodi come 'verify (userDeleter) .delete (user)', usa anche 'equals' per testare gli argomenti. Conosci qualche soluzione a questo problema? – foens

0

Non il più efficiente, ma un modo possibile è confrontare i campi usando la riflessione.

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

Sembra che la risposta di Jk1 sia semplicemente una versione ben confezionata della tua idea. È davvero così lento? – foens

+0

Il riflesso del pozzo ha i suoi costi generali, ma dipende da come e quando lo si sta usando. Penso che usarlo durante la fase di test non dovrebbe essere un problema, ma di nuovo dipende in realtà da quante chiamate riflessive fai. Inoltre, è meglio usare una libreria esistente ma conoscere l'implementazione può aiutarti a personalizzare la tua soluzione. – rocketboy

0

vedo due cose diverse:

  1. A volte non è desiderabile ignorare hashcode & uguale. E 'possibile cambiare il comportamento del programma e può danneggiare il vostro prestazioni, vedere Java Overriding hashCode() method has any Performance issue?

  2. Se non v'è alcun obbligo del cliente di ignorare hashcode & è uguale, come se fosse oggetto valore, non lo farei.Dovresti fornire un codice che soddisfi esattamente i criteri del cliente, non di più. Il tuo test dovrebbe occuparsi dell'implementazione originale nell'oggetto predefinito.