Domanda

Di recente ho iniziato a utilizzare gli strumenti di copertura del codice (in particolare Emma ed EclEmma) e mi piace molto la visione che mi dà in merito alla completezza dei test delle mie unità e alla capacità di vedere quali aree del codice sono la mia unità i test non stanno colpendo affatto. Attualmente lavoro in un'organizzazione che non effettua molti test unitari e ho intenzione di spingere davvero tutti ad affrontare test unitari, copertura del codice e TDD e, si spera, a convertire l'organizzazione.

Un problema di cui non sono sicuro su questo argomento è esattamente quanto dovrei portare la copertura del mio codice. Ad esempio, se ho una classe come questa:

//this class is meant as a pseudo-enum - I'm stuck on Java 1.4 for time being
public final class BillingUnit {

    public final static BillingUnit MONTH = new BillingUnit("month");
    public final static BillingUnit YEAR = new BillingUnit("year");

    private String value;

    private BillingUnit(String value) {
        this.value = value;
    }

    public String getValue() {
        return this.value;
    }

    public boolean equals(Object obj) {
        return value.equals(((BillingUnit) obj).getValue());

    }

    public int hashCode() {
        return value.hashCode();
    }
}

Ho scritto alcuni semplici test unitari per assicurarmi che equals () funzioni correttamente, che getValue () restituisca ciò che mi aspettavo, ecc. Ma grazie alla natura visiva di EclEmma, ??il metodo hashcode () appare in rosso brillante per "non testato".

Vale la pena preoccuparsi di provare hashCode () , in questo esempio, considerando quanto sia semplice l'implementazione? Sento che aggiungerei un test unitario per questo metodo semplicemente per aumentare la copertura del codice% in su e sbarazzarmi dell'evidente evidenziazione rossa che EclEmma aggiunge a queste linee.

Forse sto diventando nevrotico e simile a un disturbo ossessivo compulsivo, ma trovo che l'uso di qualcosa come EclEmma che rende così facile vedere ciò che non è testato - il plugin evidenzia il codice sorgente in rosso e il codice coperto in verde - rende voglio spingere per ottenere il maggior numero possibile di lezioni al 100% verde, anche quando non aggiunge molti vantaggi.

È stato utile?

Soluzione

Uso la copertura del codice per darmi suggerimenti su luoghi in cui potrei avere una serie incompleta di test. Ad esempio, posso scrivere un test per una determinata funzionalità, quindi andare a sviluppare il codice che soddisfa tale funzionalità, ma nel farlo in realtà scrivere codice che fa più di quanto si supponga - dire che potrebbe intercettare un'eccezione in un caso alternativo che il test non si esercita. Quando utilizzo l'analizzatore di copertura, vedo che ho introdotto il codice che non ha un test associato. Mi aiuta a sapere quando non ho scritto abbastanza test.

D'altra parte, l'analisi della copertura può portare a una falsa sicurezza. Avere tutto il tuo codice coperto non significa che hai abbastanza test. Devi pensare ai test dal punto di vista di cosa dovrebbe fare il codice e scrivere test per assicurarti che lo faccia. Preferibilmente scrivendo prima il test. Solo perché il tuo codice è completamente coperto non significa che il codice faccia quello che dovrebbe fare.

Nel tuo esempio, avrei scritto il test per hashCode per definire ciò che fa la funzionalità del metodo, prima di scrivere il codice. Pertanto, lo avrei coperto. Ciò non significa che ho sempre una copertura del 100%. Ad esempio, non sono eccessivamente zelante nello scrivere test per semplici accessori. Potrei anche non testare metodi della classe genitrice in cui eredito da un framework, poiché non sento la necessità di testare il codice di altre persone.

Altri suggerimenti

Penso che valga la pena usare una libreria in cui puoi scegliere di ignorare certi tipi di affermazioni. Ad esempio, se hai molti:

if(logger.isDebugEnabled()) {
    logger.debug("something");
}

È utile se è possibile disattivare i calcoli di copertura per quel tipo di linee. Può anche essere (probabilmente) valido per disattivare i calcoli di copertura per banchi e setter banali (quelli che semplicemente impostano o restituiscono una variabile membro senza altri controlli o effetti collaterali). Penso comunque che se hai sovrascritto uguali e hashcode, questi dovrebbero essere testati. Hai aggiunto funzionalità non banale e dovrebbe essere testato.

Giusto per essere chiari, il motivo per cui penso che le situazioni di cui sopra dovrebbero essere escluse dalla copertura è che:

  • Non testare che la funzionalità sia corretta. Non dovresti eseguire l'intera serie di test 5 volte, con la libreria di registrazione impostata su ciascun livello di registrazione, solo per assicurarti che tutte le tue dichiarazioni vengano colpite.
  • Se avessi fatto quanto sopra, ciò avrebbe distorto la tua copertura nell'altro modo. Se il 90% dei tuoi rami è if (log.isDebugEnabled ()) e li provi tutti ma non altri rami, sembrerà che tu abbia il 90% di copertura dei rami (buono), quando nella realtà , hai una copertura delle filiali non banale dello 0% (male !!).

Esiste una differenza tra la copertura del codice e la copertura dei test. Dovresti cercare di assicurarti che il tuo codice sia adeguatamente testato anziché avere una copertura del codice del 100%.

Considera il seguente codice:

public float reciprocal (float ex)
{
    return (1.0 / ex) ;
}

Se hai eseguito un test che ha superato il valore 1.0, otterrai una copertura del codice del 100% (ramo e istruzione) con tutti i passaggi. Il codice ha ovviamente un difetto.

Misurare la copertura dei test è più difficile e deriva dal diventare uno sviluppatore e un tester migliori.

Rispetto allo hashCode, un truist direbbe che dovresti avere un test unitario separato per quel metodo. Personalmente assicurerei che sia incluso in almeno un test di integrazione delle unità e non testerei direttamente ciascun accessorio / modificatore. Il ritorno sull'investimento è spesso troppo basso per giustificare lo sforzo. Ciò presuppone ovviamente che tu abbia un test unitario che ti assicuri di generare il codice hash corretto.

Raggiungere il 100% di copertura del codice con test significativi potrebbe non valere la pena salire, e, come menzionato prima, la copertura del codice al 100% non significa necessariamente che tutte le possibili condizioni nell'applicazione sono state testato.

Per quanto riguarda il test di uguale a , hashCode e alcune altre interfacce basate su contratto , come Comparable e serializzabili , includo questi test. È importante che il contratto equals / hashCode sia implementato correttamente, allo stesso modo con equals / Comparable .

Vedi JUnit-Addons , in particolare

Ora potrebbe non essere troppo complicato, ma un semplice controllo per verificare che funzioni ancora come previsto può essere molto utile in seguito se il metodo viene modificato.

Dal momento che il controllo dovrebbe essere davvero facile da scrivere, perché non farlo? Aiuta le statistiche e ti dà anche un controllo più avanti nel caso in cui si rompa.

Inoltre, per TDD, desideri una copertura del 100%, perché in questo modo puoi essere sicuro (o molto vicino ad esso) che non rompi nulla quando rifatti.

Un compagno programmatore bloccato come me nella vecchia Java1.4;)

Come detto nel mio precedente risposta , il codice coperto non è testato. E la conclusione è stata: da un certo punto, l'unico modo per migliorare la copertura del codice è ... eliminare il codice!

Ora, per quanto riguarda hashCode, è interessante averlo coperto in un progetto di unit test per verificare che il meccanismo ordinato ordinato sia rispettato (e non per coprire un'altra funzione)

Come ho già detto altrove, una bassa copertura del codice è un problema, ma un'elevata copertura del codice non significa che stai scrivendo oro puro.

Se non eri preoccupato della copertura del codice, suggerirei che avresti bisogno di test per equals () e getValue () - a seconda di come e dove viene utilizzato hashCode () (mi dispiace, sono uno sviluppatore C #), quindi potresti voler testarlo.

La parte più importante è che i tuoi test ti danno la certezza di aver scritto il codice giusto e che il codice funziona come previsto.

In questo caso particolare, direi che dal momento che stai testando il metodo equals, puoi anche testare che oggetti uguali hanno hashcode uguali: basta aggiungere un test extra a tutti i casi in cui si prevede che restituisca true .

Ciò ti garantirà la copertura del codice e, nell'affare, ti darà la certezza che hashCode soddisfi effettivamente il suo contratto :-)

È solo marginalmente utile, dato che ovviamente puoi fidarti del metodo hashCode di String e non ti aspetti di cambiare questa classe. Ma se sei abbastanza sospettoso del tuo metodo uguale a testarlo affatto, allora dovresti essere abbastanza sospettoso da provare che esso e l'hashCode rimangono coerenti. E dovresti sempre essere sospettoso delle ipotesi che non vorrai in futuro confondere con ciò che hai fatto prima. Ad esempio, se qualcuno arriva e decide di "ottimizzare" aggiungendo un controllo di uguaglianza del puntatore, allora potresti anche avere un set completo di test per eseguire il loro codice modificato. Altrimenti perderanno tempo per la stessa preoccupazione che hai fatto - va bene che non ho la copertura del codice?

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top