Si dovrebbe testare i valori di un enum usando i test unitari?

13

Se hai un enum con solo valori (nessun metodo come si può fare in Java), e questo enum fa parte della definizione di business del sistema, se uno scrisse unit test per questo?

Stavo pensando che dovrebbero essere scritti, anche se potrebbero sembrare semplici e ridondanti. Ritengo che ciò che riguarda le specifiche aziendali dovrebbe essere scritto esplicitamente in un test, sia con unità / integrazione / interfaccia utente / ecc. test o utilizzando il sistema di tipi della lingua come metodo di prova. Poiché i valori che un enum (ad es. In Java) deve avere, dal punto di vista dell'azienda, non possono essere testati usando il sistema di tipo, penso che ci dovrebbe essere un test unitario per questo.

Questa domanda non è simile a questo poiché non affronta lo stesso problema del mio. In quella domanda c'è una funzione aziendale (savePeople) e la persona sta indagando sull'implementazione interna (forEach). Lì, c'è un livello aziendale intermedio (la funzione salva le persone) che incapsula il costrutto del linguaggio (forEach). Qui il costrutto del linguaggio (enum) è quello usato per specificare il comportamento da un punto di vista aziendale.

In questo caso il dettaglio di implementazione coincide con la "vera natura" dei dati, ovvero: un insieme (nel senso matematico) dei valori. Potresti usare indubbiamente un set immutabile, ma gli stessi valori dovrebbero comunque essere presenti lì. Se si utilizza un array, è necessario fare la stessa cosa per testare la logica aziendale. Penso che l'enigma qui sia il fatto che la costruzione del linguaggio coincide molto bene con la natura dei dati. Non sono sicuro di essermi spiegato correttamente

    
posta IS1_SO 27.09.2017 - 15:47
fonte

9 risposte

38

If you have an enum with values only (no methods as one could do in Java), and this enum is part of the business definition of the system, should one write unit tests for it?

No, sono solo stati.

Fondamentalmente, il fatto che tu stia usando un enum è un dettaglio di implementazione ; questo è il genere di cose che potresti voler refactoring in un design diverso.

Il test delle enumerazioni per completezza è analogo al test che tutti gli interi rappresentabili sono presenti.

Verificare i comportamenti supportati dalle enumerazioni, tuttavia, è una buona idea. In altre parole, se si avvia da una suite di test in uscita e si commenta un singolo valore enum, allora almeno un test dovrebbe non riuscire (errori di compilazione considerati come errori).

    
risposta data 27.09.2017 - 16:27
fonte
17

Non testate una dichiarazione enum . È possibile verificare se l'input / output della funzione ha i valori enum previsti. Esempio:

enum Parity {
    Even,
    Odd
}

Parity GetParity(int x) { ... }

Tu non scrivi test verificando poi enum Parity definisce i nomi Even e Odd . Un test del genere sarebbe inutile dato che staresti semplicemente ripetendo ciò che è già stato detto dal codice. Dire la stessa cosa due volte non lo rende più corretto.

Tu fai scrivi test verificando GetParity dire restituirà Even per 0, Odd per 1 e così via. Questo è utile perché non stai ripetendo il codice, stai verificando il comportamento del codice, indipendentemente dall'implementazione. Se il codice all'interno di GetParity fosse completamente riscritto, i test sarebbero comunque validi. In effetti, i principali vantaggi dei test unitari sono la libertà di riscrivere e reimpostare il codice in sicurezza, garantendo che il codice funzioni come previsto.

Ma se hai un test che garantisce che una dichiarazione enum definisca i nomi attesi, allora qualsiasi modifica apportata all'enum in futuro richiederà anche di cambiare il test. Ciò significa che non è solo il doppio del lavoro, ma significa anche che qualsiasi beneficio per il test dell'unità è perso. Se devi cambiare codice e testare in contemporanea , non c'è salvaguardia contro l'introduzione di bug.

    
risposta data 27.09.2017 - 16:53
fonte
11

No, un test che verifica che un enum contenga tutti i valori validi e nient'altro sta essenzialmente ripetendo la dichiarazione dell'enumerazione. Dovresti solo provare che il linguaggio implementa correttamente il costrutto enum che è un test insensato.

Detto questo, dovresti testare il comportamento che dipende dai valori enum. Ad esempio, se si utilizzano i valori dell'enumerazione per serializzare le entità su json o qualsiasi altra cosa, o se si memorizzano i valori in un database, è necessario testare il comportamento per tutti i valori dell'enumerazione. In questo modo, se l'enum viene modificato, almeno uno dei test dovrebbe fallire. In ogni caso, ciò che verrebbe testato è il comportamento attorno all'enumerazione, non la dichiarazione enum stessa.

    
risposta data 27.09.2017 - 17:03
fonte
8

Se c'è il rischio che la modifica dell'enom romperà il tuo codice allora sicuro, qualsiasi cosa con l'attributo [Flags] in C # sarebbe un buon caso perché l'aggiunta di un valore tra 2 e 4 (3) sarebbe un bit a bit 1 e 2 piuttosto che un oggetto discreto.

È uno strato di protezione.

Dovresti considerare di avere un codice enum di pratica a cui tutti gli sviluppatori hanno familiarità. Non fare affidamento su rappresentazioni testuali dell'enum è comune, ma questo potrebbe essere in conflitto con le linee guida di serializzazione.

Ho visto persone "correggere" le lettere maiuscole delle voci enum, ordinarle in ordine alfabetico o da qualche altro raggruppamento logico che ha rotto tutti gli altri bit di codice errato.

    
risposta data 27.09.2017 - 16:19
fonte
2

Il tuo codice dovrebbe funzionare correttamente indipendentemente dai valori effettivi di un enum. Se questo è il caso, non sono necessari test di unità.

Ma potresti avere un codice in cui la modifica di un valore di enumerazione rompe le cose. Ad esempio, se un valore enum è memorizzato in un file esterno e dopo aver modificato il valore enum leggendo il file esterno si otterrà il risultato errato. In questo caso si avrà un GRANDE commento vicino all'enum che avvisa chiunque di non modificare alcun valore, e si può benissimo scrivere un test unitario che controlli i valori numerici.

    
risposta data 28.09.2017 - 00:12
fonte
1

In generale, controllare che un enum abbia un elenco di valori con hard-coded non è di grande valore, come hanno detto altre risposte, perché quindi è sufficiente aggiornare test ed enum insieme.

Una volta ho avuto il caso che un modulo usasse tipi di enum da altri due moduli e li mappasse tra loro. (Una delle enumerazioni aveva una logica aggiuntiva, l'altra era per l'accesso al DB, entrambe avevano dipendenze che dovrebbero essere isolate l'una dall'altra.)

In questo caso, ho aggiunto un test (nel modulo di mappatura) che ha verificato che tutte le voci enum nell'enumerazione sorgente esistano anche nell'enumerazione di destinazione (e quindi che la mappatura funzionerebbe sempre). (Per alcuni casi ho anche controllato il contrario.)

In questo modo, quando qualcuno ha aggiunto una voce enum a una delle enumerazioni e ha dimenticato di aggiungere la voce corrispondente all'altra, un test ha iniziato a fallire.

    
risposta data 27.09.2017 - 22:18
fonte
1

Le enumerazioni sono semplicemente tipi finiti, con nomi personalizzati (si spera che siano significativi). Un enum potrebbe avere solo un valore, come void che contiene solo null (alcuni linguaggi chiamano questo unit , e usa il nome void per un enum con elementi no !). Può avere due valori, come bool che ha false e true . Può avere tre, come colourChannel con red , green e blue . E così via.

Se due enumerazioni hanno lo stesso numero di valori, allora sono "isomorfe"; Ad esempio, se cambiamo sistematicamente tutti i nomi, possiamo usarne uno al posto di un altro e il nostro programma non si comporterà diversamente. In particolare, i nostri test non si comportano diversamente!

Ad esempio, result contenente win / lose / draw è isomorfo al colourChannel sopra, dato che possiamo sostituire ad es. colourChannel con result , red con win , green con lose e blue con draw , e finché lo facciamo ovunque (produttori e consumatori, parser e serializzatori, voci di database, file di registro, ecc.) quindi non ci saranno cambiamenti nel nostro programma. Tutti i " colourChannel test" che abbiamo scritto passeranno comunque, anche se non c'è più colourChannel !

Inoltre, se un enum contiene più di un valore, possiamo sempre riorganizzare quei valori per ottenere un nuovo enum con lo stesso numero di valori. Dato che il numero di valori non è cambiato, la nuova disposizione è isomorfa a quella vecchia, e quindi potremmo cambiare tutti i nomi e i nostri test sarebbero comunque passati (si noti che non possiamo solo cambiare la definizione, dobbiamo ancora cambiare tutti i siti di utilizzo).

Ciò significa che, per quanto riguarda la macchina, le enumerazioni sono "nomi distinguibili" e nient'altro . L'unica cosa che possiamo fare con un enum è ramificare se due valori sono uguali (ad esempio red / red ) o diversi (ad esempio red / blue ). Questa è l'unica cosa che un "test unitario" può fare, ad es.

(  red == red  ) || throw TestFailure;
(green == green) || throw TestFailure;
( blue == blue ) || throw TestFailure;
(  red != green) || throw TestFailure;
(  red != blue ) || throw TestFailure;
...

Come dice @ jesm00, tale test sta controllando l'implementazione della lingua piuttosto che il tuo programma. Questi test non sono mai una buona idea: anche se non ti fidi dell'attuazione del linguaggio, dovresti testarlo dall'esterno , dato che non può essere considerato attendibile per eseguire correttamente i test!

Quindi questa è la teoria; che dire della pratica? Il problema principale di questa caratterizzazione delle enumerazioni è che i programmi "mondo reale" sono raramente autosufficienti: abbiamo versioni legacy, implementazioni remote / embedded, dati storici, backup, database live ecc. Quindi non possiamo mai veramente "cambiare" tutte le occorrenze di un nome senza perdere alcuni usi.

Tuttavia, queste cose non sono la "responsabilità" dell'enum stesso: cambiare un enum potrebbe interrompere la comunicazione con un sistema remoto, ma al contrario potremmo risolvere un tale problema cambiando un enum!

In tali scenari, l'enumerazione è un'arma rossa: cosa succede se un sistema ha bisogno che sia questo , e un altro ha bisogno che sia tale modo? Non può essere entrambi, non importa quanti test scriviamo! Il vero colpevole è l'interfaccia di input / output, che dovrebbe produrre / consumare formati ben definiti piuttosto che "qualunque sia il valore dell'interpretazione". Quindi la vera soluzione è testare le interfacce i / o : con unit test per verificare che stia analizzando / stampando il formato previsto e con test di integrazione per verificare che il formato sia effettivamente accettato dall'altro lato .

Potremmo ancora chiederci se l'enumerazione sia stata "esercitata a sufficienza", ma in questo caso l'enumerazione è di nuovo una falsa pista. Ciò di cui siamo veramente preoccupati è la suite di test stessa . Possiamo acquisire sicurezza qui in due modi:

  • La copertura del codice può dirci se la varietà dei valori di enum provenienti dalla suite di test è sufficiente per attivare i vari rami nel codice. In caso contrario, possiamo aggiungere test che attivano i rami scoperti o generare una più ampia varietà di enumerazioni nei test esistenti.
  • Controllo proprietà può dirci se la varietà di rami nel codice è sufficiente per gestire le possibilità di esecuzione. Ad esempio, se il codice gestisce solo red e testiamo solo con red , abbiamo una copertura del 100%. Un controllore di proprietà (provate a) genererà controesempi alle nostre asserzioni, come generare i valori di green e blue che abbiamo dimenticato di testare.
  • I test di mutazione possono dirci se le nostre asserzioni in realtà controllano l'enumerazione, piuttosto che seguire semplicemente i rami e ignorarne le differenze.
risposta data 28.09.2017 - 14:59
fonte
0

Dovresti testare il comportamento osservabile del tuo codice, gli effetti delle chiamate metodo / funzione sullo stato osservabile. Finché il codice fa la cosa giusta che stai bene, non hai bisogno di testare altro.

Non è necessario affermare esplicitamente che un tipo enum abbia le voci che ti aspetti, proprio come non asserisci esplicitamente che una classe esiste realmente o che ha i metodi e gli attributi che ti aspetti.

In realtà testando il comportamento si asserisce implicitamente che esistono classi, metodi e valori coinvolti nel test, quindi non è necessario dichiararlo esplicitamente.

Nota che non hai bisogno di nomi significativi per il tuo codice per fare la cosa giusta, è solo una comodità per chi legge il tuo codice. Potresti far funzionare il tuo codice con valori enum come foo , bar ... e metodi come frobnicate() .

    
risposta data 27.09.2017 - 20:37
fonte
0

No. I test unitari servono per testare le unità.

In object-oriented programming, a unit is often an entire interface, such as a class, but could be an individual method.

link

Un test automatico per un enum dichiarato testerebbe l'integrità della lingua e la piattaforma su cui è in esecuzione piuttosto che la logica nel codice creato dallo sviluppatore. Non servirebbe a nessuno scopo utile - documentazione inclusa dal momento che il codice che dichiara l'enum funge da documentazione altrettanto bene del codice che lo testerebbe.

    
risposta data 28.09.2017 - 16:36
fonte

Leggi altre domande sui tag