Domanda

Di tanto in tanto mi imbatto in un test unitario che non afferma nulla. L'esempio particolare che ho riscontrato stamattina è stato quello di verificare che un file di registro venisse scritto quando veniva soddisfatta una condizione. Il presupposto era che se non veniva lanciato alcun errore il test veniva superato.

Personalmente non ho problemi con questo, tuttavia sembra essere un po 'un "odore di codice" per scrivere un unit test a cui non sono associate asserzioni.

Ti stai solo chiedendo quali sono le opinioni della gente su questo?

È stato utile?

Soluzione

Questo sarebbe il modo ufficiale per farlo:

// Act
Exception ex = Record.Exception(() => someCode());

// Assert
Assert.Null(ex);

Altri suggerimenti

È semplicemente un test molto minimo e dovrebbe essere documentato come tale. Verifica solo che non esploda quando viene eseguito. La parte peggiore di test come questo è che presentano un falso senso di sicurezza. La copertura del codice aumenterà, ma è illusoria. Odore molto cattivo.

Se non c'è nessuna affermazione, non è un test.

Smetti di essere pigro - potrebbe volerci un po 'di tempo per capire come ottenere l'affermazione lì dentro, ma vale la pena sapere che ha fatto quello che ti aspettavi che facesse.

Questi sono conosciuti come test del fumo e sono comuni. Sono controlli di sanità di base. Ma non dovrebbero essere gli unici tipi di test che hai. Avresti ancora bisogno di una sorta di verifica in un altro test.

Un simile test ha un odore. Dovrebbe verificare che il file sia stato scritto, almeno che l'ora modificata sia stata forse aggiornata.

Ho visto alcuni test scritti in questo modo che alla fine non hanno testato nulla, cioè il codice non ha funzionato, ma non è nemmeno esploso.

Se hai dei requisiti espliciti per cui il codice in prova non genera un'eccezione e vuoi esplicitare questo fatto (test come documenti dei requisiti), farei qualcosa del genere:

try
{
  unitUnderTest.DoWork()
}
catch
{
  Assert.Fail("code should never throw exceptions but failed with ...")
}

... ma questo mi profuma ancora un po ', probabilmente perché sta cercando di dimostrare un aspetto negativo.

In un certo senso, stai affermando implicitamente che il codice non genera un'eccezione. Ovviamente sarebbe più prezioso afferrare effettivamente il file e trovare la riga appropriata, ma suppongo che qualcosa sia meglio di niente.

In generale, vedo che ciò accade nei test di integrazione, solo il fatto che qualcosa sia riuscito al completamento è abbastanza buono. In questo caso, mi piace.

Immagino che se lo vedessi più volte nei test unitari sarei curioso di sapere quanto siano stati utili i test.

MODIFICA: Nell'esempio fornito dall'OP, ci sono alcuni risultati verificabili (risultato del file di registro), quindi supponendo che se non viene generato alcun errore, il funzionamento è pigro.

Può essere una buona soluzione pragmatica, soprattutto se l'alternativa non è affatto un test.

Il problema è che il test passerebbe se tutte le funzioni chiamate fossero no-ops. Ma a volte non è possibile verificare che gli effetti collaterali siano quelli che ti aspettavi. Nel mondo ideale ci sarebbe abbastanza tempo per scrivere i controlli per ogni test ... ma io non abito lì.

L'altro posto in cui ho usato questo modello è quello di incorporare alcuni test delle prestazioni con i test unitari perché era un modo semplice per farli eseguire su ogni build. I test non affermano nulla, ma misurano quanto tempo ha richiesto il test e registralo.

Ho già visto qualcosa del genere prima e penso che sia stato fatto solo per sostenere i numeri di copertura del codice. Probabilmente non sta davvero testando il comportamento del codice. In ogni caso, sono d'accordo sul fatto che (l'intenzione) dovrebbe essere documentato nel test per chiarezza.

Il nome del test dovrebbe documentarlo.

void TestLogDoesNotThrowException(void) {
    log("blah blah");
}

In che modo il test verifica se il registro è scritto senza asserzioni?

Devo ammettere che non ho mai scritto un unit test per verificare che stavo registrando correttamente. Ma ci ho pensato e mi sono imbattuto in questa discussione di come potrebbe essere fatto con JUnit e Log4J. Non è troppo carino ma sembra che funzionerebbe.

I test dovrebbero sempre far valere qualcosa, altrimenti cosa stai dimostrando e come puoi riprodurre in modo coerente prove del funzionamento del tuo codice?

Lo facciamo sempre. Deridiamo le nostre dipendenze usando JMock, quindi immagino in un certo senso che il framework JMock stia facendo l'affermazione per noi ... ma va qualcosa del genere. Abbiamo un controller che vogliamo testare:

Class Controller {
  private Validator validator;

  public void control(){
    validator.validate;
  }

  public setValidator(Validator validator){ this.validator = validator; }
}

Ora, quando testiamo Controller non vogliamo testare Validator perché ha i suoi test. quindi abbiamo un test con JMock solo per assicurarci di chiamare validate:

public void testControlShouldCallValidate(){
  mockValidator.expects(once()).method("validate");
  controller.control;
}

E questo è tutto, non esiste alcuna "affermazione" per vedere, ma quando si chiama il controllo e il " validate " il metodo non viene chiamato, quindi il framework JMock genera un'eccezione (qualcosa come "metodo atteso non invocato" o qualcosa del genere).

Abbiamo quelli dappertutto. È un po 'arretrato poiché in pratica hai impostato la tua asserzione POI effettua la chiamata al metodo testato.

A volte utilizzo il mio framework di scelta unit test (NUnit) per creare metodi che fungono da punti di ingresso in parti specifiche del mio codice. Questi metodi sono utili per la profilazione delle prestazioni, del consumo di memoria e del consumo di risorse di un sottoinsieme del codice.

Questi metodi non sono sicuramente test unitari (anche se sono contrassegnati con l'attributo [Test] ) e sono sempre contrassegnati per essere ignorati e documentati esplicitamente quando vengono controllati nel controllo del codice sorgente.

Occasionalmente utilizzo questi metodi anche come punti di ingresso per il debugger di Visual Studio. Uso Resharper per passare direttamente al test e quindi al codice che voglio eseguire il debug. Questi metodi o non arrivano fino al controllo del codice sorgente, oppure acquisiscono le loro affermazioni.

My " real " i test unitari sono costruiti durante i normali cicli TDD e affermano sempre qualcosa, anche se non sempre direttamente - a volte le asserzioni fanno parte del framework beffardo, e talvolta sono in grado di rielaborare asserzioni simili in un unico metodo. I nomi di questi metodi refactored iniziano sempre con il prefisso " Assert " per renderlo ovvio per me.

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