2010-10-22 14 views
16

Sto usando Boost Test per testare alcuni codici C++.Come confrontare i vettori con Boost.Test?

Ho un vettore di valori che ho bisogno di confrontare con i risultati attesi, ma non voglio controllare manualmente i valori in un ciclo:

BOOST_REQUIRE_EQUAL(values.size(), expected.size()); 

for(int i = 0; i < size; ++i) 
{ 
    BOOST_CHECK_EQUAL(values[i], expected[i]); 
} 

Il problema principale è che il controllo del ciclo doesn stampare l'indice, quindi è necessario un po 'di ricerca per trovare la mancata corrispondenza.

Potrei usare std::equal o std::mismatch sui due vettori, ma ciò richiederà anche molta piastra di riscaldamento.

C'è un modo più pulito per fare questo?

risposta

30

Utilizzare BOOST_CHECK_EQUAL_COLLECTIONS. È una macro in test_tools.hpp che prende due coppie di iteratori:

BOOST_CHECK_EQUAL_COLLECTIONS(values.begin(), values.end(), 
           expected.begin(), expected.end()); 

segnalerà gli indici ei valori che disadattamento. Se le dimensioni non corrispondono, lo segnalerà anche (e non si esaurirà semplicemente alla fine del vettore).


Si noti che se si desidera utilizzare o BOOST_CHECK_EQUALBOOST_CHECK_EQUAL_COLLECTIONS con i tipi non-POD, è necessario implementare

bool YourType::operator!=(const YourType &rhs) // or OtherType 
std::ostream &operator<<(std::ostream &os, const YourType &yt) 

per il confronto e la registrazione, rispettivamente.
L'ordine degli iteratori passati a BOOST_CHECK_EQUAL_COLLECTIONS determina quale è l'RHS e il LHS del confronto != - il primo intervallo di iteratore sarà il LHS nei confronti.

+2

questo è nella documentazione, è solo ben nascosto –

+1

Sì, sono impegnato a cancellare la mia vergogna. :) Grazie per i tuoi esempi nella tua risposta. – mskfisher

+0

È un po 'troppo invadente forzare il sistema sotto test per implementare 'operator! =' E 'operator <<' solo per soddisfare Boost.Test, IMO. Inoltre, non è possibile definire queste funzioni membro per 'std :: vector'. Vedi la mia risposta qui sotto per una soluzione migliore. – legalize

10

Che ne dici di BOOST_CHECK_EQUAL_COLLECTIONS?

BOOST_AUTO_TEST_CASE(test) 
{ 
    int col1 [] = { 1, 2, 3, 4, 5, 6, 7 }; 
    int col2 [] = { 1, 2, 4, 4, 5, 7, 7 }; 

    BOOST_CHECK_EQUAL_COLLECTIONS(col1, col1+7, col2, col2+7); 
} 

esempio

Esecuzione caso 1 test ...

test.cpp (11): errore di "test": controllare {col1, col1 + 7} {== col2 , col2 + 7} fallito.

incongruenze in posizione 2: 3 = 4

incongruenze in posizione 5: 6 = 7

* 1 guasto rilevato nella prova suite "esempio"

9

Un po 'fuori tema, tuttavia, quando a volte è necessario confrontare raccolte di numeri in virgola mobile usando comparison with tolerance allora questo snippet può essere utile:

// Have to make it a macro so that it reports exact line numbers when checks fail. 
#define CHECK_CLOSE_COLLECTION(aa, bb, tolerance) { \ 
    using std::distance; \ 
    using std::begin; \ 
    using std::end; \ 
    auto a = begin(aa), ae = end(aa); \ 
    auto b = begin(bb); \ 
    BOOST_REQUIRE_EQUAL(distance(a, ae), distance(b, end(bb))); \ 
    for(; a != ae; ++a, ++b) { \ 
     BOOST_CHECK_CLOSE(*a, *b, tolerance); \ 
    } \ 
} 

Questo non stampa gli indici di array di elementi di disallineamento, ma stampa i valori di mismatching con alta precisione, in modo che siano spesso facili da trovare.

Esempio di utilizzo:

auto mctr = pad.mctr(); 
std::cout << "mctr: " << io::as_array(mctr) << '\n'; 
auto expected_mctr{122.78731602430344,-13.562000155448914}; 
CHECK_CLOSE_COLLECTION(mctr, expected_mctr, 0.001); 
+0

Per utilizzarlo con Boost :: Ublas :: Vectors è necessario regolare la macro, ma l'idea è buona. –

3

È possibile utilizzare BOOST_REQUIRE_EQUAL_COLLECTIONS con std::vector<T>, ma si deve insegnare Boost.Test come stampare un std::vector quando si dispone di un vettore di vettori o di una mappa i cui valori sono vettori. Quando hai una mappa, Boost.Test deve essere insegnato come stampare std::pair. Poiché non è possibile modificare la definizione di std::vector o std::pair, è necessario farlo in modo che l'operatore di inserimento del flusso definito dall'utente venga utilizzato da Boost.Test senza far parte della definizione di classe di std::vector. Inoltre, questa tecnica è utile se non si desidera aggiungere operatori di inserimento del flusso al sistema in prova solo per rendere felice Boost.Test.

Ecco la ricetta per qualsiasi std::vector:

namespace boost 
{ 

// teach Boost.Test how to print std::vector 
template <typename T> 
inline wrap_stringstream& 
operator<<(wrap_stringstream& wrapped, std::vector<T> const& item) 
{ 
    wrapped << '['; 
    bool first = true; 
    for (auto const& element : item) { 
     wrapped << (!first ? "," : "") << element; 
     first = false; 
    } 
    return wrapped << ']'; 
} 

} 

Questo formatta i vettori come [e1,e2,e3,...,eN] per un vettore con N elementi e funziona per qualsiasi numero di vettori annidati, ad esempio dove gli elementi del vettore sono anche vettori.

Ecco la ricetta simile per std::pair:

namespace boost 
{ 

// teach Boost.Test how to print std::pair 
template <typename K, typename V> 
inline wrap_stringstream& 
operator<<(wrap_stringstream& wrapped, std::pair<const K, V> const& item) 
{ 
    return wrapped << '<' << item.first << ',' << item.second << '>'; 
} 

} 

BOOST_REQUIRE_EQUAL_COLLECTIONS vi dirà l'indice degli elementi non corrispondenti, così come i contenuti delle due collezioni, assumendo le due collezioni sono della stessa dimensione. Se sono di dimensioni diverse, allora si considera una mancata corrispondenza e vengono stampate le diverse dimensioni.

+0

Buon punto, grazie per averlo menzionato. Ho dovuto aggiungere più volte codice simile ai miei test di unità. – mskfisher

+0

Con boost 1.33 si dovrebbe usare wrap_stringstream :: wrapped_stream invece di wrap_stringstream, nel codice sopra. –

2

Dal momento che Boost 1.59 è molto più facile confrontare istanze std::vector. Vedi this documentation per la versione 1.63 (che è quasi uguale a questo riguardo a 1.59).

Ad esempio, se si hanno dichiarato std::vector<int> a, b; è possibile scrivere

BOOST_TEST(a == b); 

per ottenere un confronto molto di base. Il lato negativo di questo è che in caso di fallimento Boost ti dice solo che a e non sono la stessa cosa. Ma si ottiene più informazioni confrontando elemento-saggio che è possibile in modo elegante

BOOST_TEST(a == b, boost::test_tools::per_element()); 

Oppure, se si vuole un confronto lessicografico si può fare

BOOST_TEST(a <= b, boost::test_tools::lexicographic()); 
+0

Eccellente, grazie! Sono anche felice di vedere BOOST_TEST_CONTEXT, che sembra essere stato aggiunto in 1.59: http://www.boost.org/doc/libs/1_59_0/libs/test/doc/html/boost_test/test_output/contexts.html – mskfisher

Problemi correlati