2010-05-06 14 views
5

Come testare l'unità do_int_to_string_conversion?L'unità verifica una funzione i cui scopi sono gli effetti collaterali

#include <string> 
#include <iostream> 

void do_int_to_string_conversion(int i, std::string& s) { 
    switch(i) { 
    case 1: 
     s="1"; 
     break; 
    case 2: 
     s="2"; 
     break; 
    default: 
     s ="Nix"; 
    } 
} 

int main(int argc, char** argv){ 
    std::string little_s; 

    do_int_to_string_conversion(1, little_s); 
    do_int_to_string_conversion(2, little_s); 
    do_int_to_string_conversion(3, little_s); 

} 
+0

Assicurati solo che l'effetto desiderato si sia verificato dopo la chiamata? – jball

+2

Suppongo che "effetto collaterale" in questo esempio si riferisca all'output di std :: cout. –

+0

Devo chiedere, però, perché non si restituisce semplicemente una stringa anziché passare un riferimento? Stai sostituendo completamente quello che hai inserito, in modo da non trarre vantaggio dalle prestazioni evitando la copia (dal momento che non lo eviti), e se hai restituito la stringa, potresti beneficiare di RVO (ottimizzazione del valore di ritorno) . –

risposta

9

Presumo che questo sia solo un esempio. Perché non riesci a far valere il valore di little_s dopo ogni chiamata?

do_int_to_string_conversion(1, little_s); 
assert_are_equal("1", little_s); 
+1

Ottima idea. – David

-1

Si può usare qualcosa di simile Expect farla passare alcuni input e verificare che la sua uscita è quello che dovrebbe essere.

3

Se davvero necessità di garantire che l'uscita è stato scritto, è necessario rompere la vostra dipendenza da std::cout e utilizzare un altro std::ostream durante i test.

Questo potrebbe essere semplice come una variabile globale:

#if PRODUCTION 
std::ostream my_output = std::cout; 
#else 
std::ostream my_output = std::ostringstream; 
#endif 

void setup() 
{ 
    my_output = std::ostringstream; 
} 

void print_hello() 
{ 
    my_output << "hello"; 
} 

void test_hello_was_printed() 
{ 
    print_hello(); 
    ASSERT("hello" == my_output.str()); 
} 

o qualcosa di simile in tal senso.

+0

Questa è anche una buona idea e non l'ho presa in considerazione. Il diritto a std :: cout è superfluo [colpa mia per averlo incluso]. – David

3

Cambierei do_int_to_string_conversion in modo che lo fa solo una cosa (convertire l'in una stringa).

void do_int_to_string_conversion(int i, std::string& s) { 
    switch(i) { ... } 
} 

Questo non ha effetti collaterali, in modo da poter scrivere un semplice test di unità che verifica l'uscita (s).

Se avevo bisogno di una funzione che ha stampato il risultato della conversione, che avevo messo in una funzione separata, e mi piacerebbe parametrizzare il flusso di output.

void output_int(int i, ostream &stream) { 
    std::string s; 
    do_int_to_string_conversion(i, s); 
    stream << s; 
} 

All'unità di test, passerei in un oggetto std :: stringstream e controllare il risultato.

+0

@Adrian: +1 - ci si batte. –

11

Invece di preoccuparsi di come testare la funzione così com'è, mi piacerebbe ridisegnare la funzione di lavorare un po 'più sensibilmente, e testare la versione ri-progettato, invece.

Al momento, la funzione sembra avere tre distinte responsabilità (e solo leggermente correlati): fare una conversione, modificare una stringa fornita esternamente, e scrivere alcuni dati in un flusso. Anche il flusso a cui scrive (std::cout) è hardcoded: si tratta di un problema in attesa (ad esempio, la conversione in un ambiente GUI è probabilmente non banale).

Vorrei iniziare con 1) dividerlo in funzioni logiche e 2) fornire il flusso come parametro.

std::string convert_int(int val) { 
    switch (val) { 
     case 1: return "1"; 
     case 2: return "2"; 
     default: return "Nix"; 
    } 
} 

std::ostream &write_string(std::ostream &os, std::string const &s) { 
    return os << s; 
} 

non ho incluso nulla a che (in particolare) modificare una stringa di alimentazione esterna - ovviamente è possibile assegnare il valore restituito da convert_int come meglio credi, e il valore della stringa che è stato passato in wasn' Comunque sia usato.

Sinceramente, write_string è un buon candidato da eliminare completamente, ma dal momento che hai avuto quel tipo di capacità di base, lo terremo per il momento. Il test di questi è relativamente semplice: per convert_int, esaminiamo la stringa che restituisce e confrontiamo con ciò che ci aspettiamo. Per write_string, possiamo passare un stringstream anziché un normale ostream - quindi possiamo usare .str() per ottenere quel risultato come stringa e (di nuovo) confrontare a quello che ci aspettiamo.

+1

La stringa in questo caso è un proxy per qualcosa di più grande, ed esterna alle cose su cui sto lavorando. Hai chiesto nei commenti dell'ultima domanda che ho chiesto di includere un piccolo esempio rappresentativo compilabile del problema. Questo è lo snippet di questo codice. – David

+1

@David: ok, non è proprio un grosso problema. Del resto, modificare l'oggetto (di qualunque tipo) che è stato passato per riferimento non è necessariamente un problema. Vuoi comunque progettare la funzione per fare * una cosa * però. Se modifica un oggetto, crea un oggetto "pulito", fallo fare la sua modifica, quindi controlla il risultato - anche se se il suo scopo principale è modificare un oggetto, potresti voler pensare se dovrebbe essere un membro di quel oggetto. –

+0

Ho fatto questa domanda ad un altro mio amico, e lui ha avuto una risposta simile a voi ragazzi. Includere la scrittura su std :: cout è stato male da parte mia perché confondeva le cose. Sto ancora lavorando alle mie capacità di chiedere domande. La stringa in questo caso è un proxy per la struttura di dati mappata su memoria grande che vive nell'interfaccia con quello su cui stiamo lavorando e la pila di vecchio fortran. L'int indicizza l'elenco di possibili cose da scrivere in quella posizione. – David

Problemi correlati