2010-07-30 13 views
10

Qualsiasi codice può fornire effetti collaterali. Il più delle volte, gli effetti collaterali possono essere un segno di una cattiva progettazione e/o necessità di refactoring, ma durante i test unitari trovo difficile confrontarmi. Si consideri il seguente esempio:Esiste un modo per testare l'unità contro gli effetti collaterali?

[Test] 
public void TrimAll_Removes_All_Spaces() 
{ 
    // Arrange 
    var testSubject = "A string with  lots of  space"; 
    var expectedResult = "Astringwithlotsofspace"; 

    // Act 
    var result = testSubject.TrimAll(); 

    // Assert 
    Assert.AreEqual(expectedResult, result); 
} 

che verifica la seguente estensione:

public static string TrimAll(this string str) 
{ 
    PokeAround(); 

    return str.Replace(" ", ""); 
} 

Il test passeranno, ma non v'è alcuna protezione effetti collaterali agains. Gli effetti della chiamata a PokeAround passeranno completamente inosservati.

Dato che non si sa cosa sia PokeAround - potrebbe essere qualsiasi cosa! - come scrivi un test che lo difende? è tutto possibile?

Chiarimento: Ci sono stati un paio di commenti sul PokeAround come completamente sconosciuto essendo uno scenario molto improbabile, dal momento che abbiamo la fonte quando scriviamo il test. Il motivo per cui ho fatto questa domanda, però, era trovare un modo per proteggersi dagli effetti collaterali aggiunti in seguito su. Cioè, quando scrivo il test, potrei avere l'aspetto metodo Exension come questo:

public static string TrimAll(this string str) 
{ 
    return str.Replace(" ", ""); 
} 

Il test viene superato, tutto è buono. Poi, un mese dopo, quando sono in ferie, un collega aggiunge la chiamata PokeAround. Voglio che il test che ho già scritto fallisca perché lo ha fatto.

+0

La domanda è priva di senso. Non puoi inserire il codice di test a cui non hai accesso. "Nella programmazione del computer, il test unitario è un metodo mediante il quale vengono testate singole unità del codice sorgente per determinare se sono idonee all'uso.Un'unità è la più piccola parte testabile di un'applicazione.Nella programmazione procedurale un'unità può essere un individuo funzione o procedura I test delle unità vengono creati dai programmatori o occasionalmente dai tester della scatola bianca. " – WOPR

+0

@WOPR: vedere il mio aggiornamento per uno scenario del mondo reale in cui è pertinente. –

+2

È ancora privo di senso. Ora stai cercando di estendere il concetto di unit test per prevenire l'idiozia in altri programmatori. Il tuo "futuro collega" dovrebbe avere unità testate anche le sue modifiche. Non è possibile utilizzare il test delle unità per verificare le modifiche future ... potrebbe anche avere cancellato tutto il codice e modificato per eliminare il record di avvio principale. – WOPR

risposta

9

Questo è ciò che viene chiamato rilevamento in Working Effectively With Legacy Code. Cioè, percependo gli effetti della chiamata al metodo testato.

Dato che non sai cosa sia PokeAround - potrebbe essere qualsiasi cosa!

Dal momento che stiamo parlando di test di unità, questo dovrebbe essere vero difficilmente - test delle unità è whitebox test, e il codice è (dovrebbe essere) lì per voi per controllare. A meno che non si trovi in ​​una libreria di terze parti di tipo closed source, nel qual caso non è necessario eseguire il test 10 non è possibile testarlo unitamente per definizione (forse sono necessari test di funzionalità/accettazione, ma è una questione completamente diversa ... .).

Aggiornamento: così si desidera assicurarsi che le future modifiche al metodo testato dell'unità non avranno mai effetti collaterali inattesi? Penso che

  1. non può,
  2. non dovrebbe.

Non è possibile, perché non esiste un modo sensato per rilevare la mancanza di effetti collaterali da una chiamata di metodo in un programma di vita reale (non banale). Quello che stai cercando è verificare che lo stato di dell'intero universo non sia cambiato a parte questo e questa piccola cosa. Anche dal punto di vista di un umile programma, quell'universo è vasto. Una chiamata al metodo può creare/aggiornare/eliminare qualsiasi numero di oggetti locali (molti dei quali non è possibile vedere dall'ambiente di test dell'unità), toccare i file sui file system locali/di rete disponibili, eseguire richieste DB, effettuare chiamate di procedure remote. ..

Non dovresti, perché spetta al tuo collega fare quel cambiamento futuro per occuparsi dell'unità di testare il suo cambiamento. Se non ti fidi che questo accadrà, hai un problema di persone o di processi, non un problema di test unitario.

+4

@ Péter Török, in realtà ** devi ** testare le librerie di terze parti. La biblioteca potrebbe, e probabilmente cambierà ad un certo punto. Con test scritti che confermano le tue ipotesi su come funziona, puoi aggiornare la libreria in modo sicuro e sapere se ci sono stati cambiamenti nel comportamento previsto immediatamente, e non dopo che è andato in produzione quando gli utenti iniziano a chiamare. – CaffGeek

+0

Penso che l'ultima frase debba essere riformulata in: "A meno che non si trovi in ​​una libreria di terze parti, nel qual caso non dovresti * * * * * * * * (in un mondo perfetto). – strager

+0

Vedere il mio aggiornamento per uno scenario reale in cui è pertinente. –

0

Si potrebbe guardare in un altro modo, che scrivendo un test di unità per questo codice si sta costringendo a riconoscere un problema nel codice (effetti collaterali). Potresti quindi riscrivere/ristrutturare il codice in modo tale che sia testabile, eventualmente spostando PokeAround nella sua funzione, o semplicemente spostandolo dal codice che stai provando a testare, se ha bisogno di più input/stato da verificabile.

0

Non sono sicuro che potreste. Dopo tutto, se si tratti di un effetto previsto o di un effetto collaterale dipende dall'intenzione del progettista.

Suggerirei di affermare che il codice "side-effect" è invariato.

Inoltre, uno strumento come Jester potrebbe essere di aiuto, ma non credo che farebbe la differenza nel tuo esempio.

0

Dipende da cosa intendi per effetto collaterale - Voglio dire, forse PokeAround() fa qualcosa di importante che deve essere fatto. Come classifichi un effetto collaterale?

In ogni caso, non c'è una particolare tecnologia/tecnica di cui sono a conoscenza per proteggersi dagli effetti collaterali in un test di unità singola, ma finché si avrà copertura di test per tutto il codice, si spera che eventuali effetti collaterali indesiderati essere prelevato da almeno un test.

Gli strumenti di test BDD/Integration aiuteranno anche in questo, poiché (di solito) testano aree di funzionalità più ampie e non solo singole classi/metodi.

Una cosa che si potrebbe desiderare di guardare è Design By Contract (DBC). Ciò consente di specificare le condizioni pre e post e anche le invarianti, in modo che se un metodo viene mai chiamato con parametri non validi, restituisce valori non validi o l'oggetto si trova in uno stato non valido, verrà generato un errore di qualche tipo.

0

No non è possibile. Test per gli effetti collaterali è difficile in qualsiasi fase di test. Differisce in diversi progetti (sviluppo del prodotto, sviluppo di strumenti, sviluppo di applicazioni aziendali, sviluppo di giochi, ecc.).

In assenza di un test di regressione completo, non è stato possibile rilevare gli effetti collaterali.

In un tipico progetto (ho sperimentato) la domanda per "questo cambiamento ha effetti collaterali" viene spesso richiesta verso la fine del progetto (vicino ad andare in diretta) o quando qualcuno vuole aggiungere una hotfix in un sistema già produttivo. Ho scoperto che senza un test di regressione l'unica misura di controllo di qualità (ancora rischiosa) è la revisione del codice.

Spero che aiuti.

2

Dato che non sai cosa sia PokeAround - potrebbe essere qualsiasi cosa! - come scrivi un test che lo difende? è tutto possibile?

Questa domanda è speciosa. È improbabile che la situazione si verifichi nel mondo reale.

  1. Sai sempre cos'è PokeAround. È un test unitario. Hai la fonte.

    Se - attraverso qualche male organizzativo - è proibito leggere la fonte, si ha un problema organizzativo, non un problema tecnico.

    Se non sai cosa sia PokeAround, hai persone che sono in particolare cattive e impediscono il successo. Hanno bisogno di nuovi posti di lavoro. O lo fai.

  2. È necessario utilizzare Mock per questo PokeAround in modo da poter osservare gli effetti collaterali.

"guardia contro gli effetti collaterali aggiunto in seguito."

Questo non è un esempio di un pezzo di codice misterioso. Sai ancora cos'è PokeAround. Sai sempre cos'è PokeAround.

Ecco perché eseguiamo test di regressione. http://en.wikipedia.org/wiki/Regression_testing

È ancora il test dell'unità.

  • È ancora possibile testare PokeAround con un test di unità stand-alone.

  • E si testano le cose che utilizzano PokeAround con una simulazione di PokeAround.

+1

* "Sai sempre cos'è PokeAround. È un test unitario. Hai la fonte." * - Se avessi sempre saputo cosa stava facendo PokeAround (e qualsiasi altra funzione che codifico), non avrei motivo di scrivere Unit -Test a tutti. So solo cosa devono * fare *. –

+1

@Luther Blissett: "Se avessi sempre saputo cosa stava facendo PokeAround (e qualsiasi altra funzione che codifico), non avrei motivo di scrivere un test unitario" Cosa? Il test non è * scoperta *. Il test è la prova che in realtà fa ciò che afferma. Il test non è qualcosa che fai per scoprire misteri interiori del codice. –

+0

Vedere il mio aggiornamento per uno scenario reale in cui ciò è rilevante. –

0

Una tecnica consiste nel randomizzare l'ordine dei test di unità. Se la tua suite di test è ragionevolmente completa, la randomizzazione degli ordini dei test può rivelare effetti collaterali inattesi, test che dipendono erroneamente dallo stato dei test precedenti e così via.

Google Test (per C++) può do this; Non conosco altri framework che hanno questa funzione.

2

Nessuna esperienza di prima mano da me, ma questo potrebbe interessarvi: Codice Contratti

http://research.microsoft.com/en-us/projects/contracts/

ha una disposizione per mettere un attributo [pura] a un metodo e far rispettare effetto collaterale freeness attraverso runtime o compilare i controlli orari. Permette inoltre di specificare e applicare una serie impressionante di altri vincoli.

+0

Oh, interessante! – strager

1

Io non programma in questa lingua, ma quanto segue sarebbe un modo per verificare se la stringa originale è stato modificato:

[Test] 
public void TrimAll_Removes_All_Spaces() 
{ 
    // Arrange 
    var testSubject = "A string with  lots of  space"; 
    var testSubjectCopy = "A string with  lots of  space"; 
    var expectedResult = "Astringwithlotsofspace"; 

    // Act 
    var result = testSubject.TrimAll(); 

    // Assert 
    Assert.AreEqual(expectedResult, result); 

    // Check for side effects 
    Assert.AreEqual(testSubjectCopy, testSubject); 
} 
2

Tenete a mente che i test di unità sono solo uno strumento in un arsenale di verifiche e controlli, alcuni dei quali possono catturare del tuo collega "PokeAround":

  1. unit test
  2. Codice Ispezioni/Recensioni
  3. design by Cont RACT
  4. asserzioni
  5. statica strumento di analisi come FindBugs e il Checker Framework (@NonNull, @Pure e @ReadOnly tutto rock!)

altro?

Il test è valido, tutto è buono. Poi, un mese dopo, quando sono in vacanza, un collega aggiunge la chiamata PokeAround. Voglio che il test che ho già scritto fallisca perché lo ha fatto.

Cosa ti fa pensare che il vostro collega non cambierebbe il test come bene?

+1

Assumibilmente il collega è incompetente, non malizioso. –

Problemi correlati