2011-10-06 7 views
11

Ho iniziato usando googletest per implementare i test e ci siamo imbattuti in questa citazione nella documentazione relativa value-parameterized testsIl test guidato dai dati è errato?

  • Si desidera testare il proprio codice su vari ingressi (anche noto come data-driven test). Questa funzione è facile da abusare, quindi si prega di esercitare il buon senso quando lo si fa!

penso che sto davvero "abusando" il sistema quando si fa la seguente e vorrei sentire il vostro input e opinioni su questa materia.

Supponiamo di avere il seguente codice:

template<typename T> 
struct SumMethod { 
    T op(T x, T y) { return x + y; } 
}; 

// optimized function to handle different input array sizes 
// in the most efficient way 
template<typename T, class Method> 
T f(T input[], int size) { 
    Method m; 
    T result = (T) 0; 
    if(size <= 128) { 
     // use m.op() to compute result etc. 
     return result; 
    } 
    if(size <= 256) { 
     // use m.op() to compute result etc. 
     return result; 
    } 
    // ... 
} 

// naive and correct, but slow alternative implementation of f() 
template<typename T, class Method> 
T f_alt(T input[], int size); 

Ok, quindi con questo codice, certamente il senso di testare f() (per confronto con f_alt()) con differenti formati di matrice di ingresso di dati generati casualmente per verificare la correttezza dei rami. In cima a quello, ho diversi structs come SumMethod, MultiplyMethod, ecc, quindi sono l'esecuzione di un numero piuttosto elevato di test anche per i diversi tipi:

typedef MultiplyMethod<int> MultInt; 
typedef SumMethod<int> SumInt; 
typedef MultiplyMethod<float> MultFlt; 
// ... 
ASSERT(f<int, MultInt>(int_in, 128), f_alt<int, MultInt>(int_in, 128)); 
ASSERT(f<int, MultInt>(int_in, 256), f_alt<int, MultInt>(int_in, 256)); 
// ... 
ASSERT(f<int, SumInt>(int_in, 128), f_alt<int, SumInt>(int_in, 128)); 
ASSERT(f<int, SumInt>(int_in, 256), f_alt<int, SumInt>(int_in, 256)); 
// ... 
const float ep = 1e-6; 
ASSERT_NEAR(f<float, MultFlt>(flt_in, 128), f_alt<float, MultFlt>(flt_in, 128), ep); 
ASSERT_NEAR(f<float, MultFlt>(flt_in, 256), f_alt<float, MultFlt>(flt_in, 256), ep); 
// ... 

Ora, naturalmente, la mia domanda è: questo fa qualsiasi senso e perché questo sarebbe male?

In realtà, ho trovato un "bug" durante l'esecuzione di test con float s dove f() e f_alt() darebbe valori diversi con SumMethod a causa degli arrotondamenti, che ho potuto migliorare di preselezione della matrice di ingresso, ecc .. Da questa esperienza Considero questo in qualche modo una buona pratica.

risposta

10

Penso che il problema principale sia il test con "dati generati casualmente". Non è chiaro dalla tua domanda se questi dati vengano rigenerati ogni volta che viene eseguito il cablaggio del test. Se lo è, i risultati del test non sono riproducibili. Se qualche test fallisce, dovrebbe fallire ogni volta che lo esegui, non una volta in una luna blu, su qualche strana combinazione di dati di test casuali.

Quindi, a mio parere, è necessario pre-generare i dati di test e conservarli come parte della suite di test. È inoltre necessario assicurarsi che il set di dati sia sufficientemente ampio e sufficientemente diversificato per offrire una copertura di codice sufficiente.

Inoltre, come Ben Voigt ha commentato di seguito, il test con dati casuali solo non è sufficiente. È necessario identificare casi angolari nei propri algoritmi e testarli separatamente, con dati personalizzati appositamente per questi casi. Tuttavia, a mio parere, ulteriori test con dati casuali sono anche utili quando/se non si è sicuri di conoscere tutti i casi d'angolo. Potresti colpirli per caso usando dati casuali.

+2

I dati generati casualmente sono negativi per due motivi: in primo luogo, perché come hai detto, i test non sono riproducibili. E in secondo luogo, perché i casi angolari potrebbero non essere coperti da dati generati casualmente. Salvare i vettori casuali non fa nulla per il secondo inconveniente. –

+0

Grazie. Ho modificato la mia risposta, hai ragione, ovviamente. – haimg

+0

@haimg - Se stai facendo un test della scatola nera, come fai a sapere l'algoritmo usato e i suoi casi d'angolo? :-) –

6

Il problema è che non si può affermare la correttezza su float nello stesso modo in cui si eseguono.

Verificare la correttezza all'interno di un certo epsilon, che è una piccola differenza tra i valori calcolati e previsti. Questo è il meglio che puoi fare. Questo è vero per tutti i numeri in virgola mobile.

penso di essere davvero "abusando" il sistema quando si fa il seguente

Ti è che questo è stato male prima di leggere questo articolo? Puoi articolare cosa c'è di male in questo?

È necessario testare questa funzionalità a volte. Hai bisogno di dati per farlo. Dov'è l'abuso?

+0

Sicuro. Ho appena dimenticato di inserirlo correttamente nell'esempio precedente. Modificato. A parte questo, sono più interessato agli argomenti contro la scrittura di questo tipo di test. – bbtrb

0

Uno dei motivi per cui potrebbe essere negativo è che i test basati sui dati sono più difficili da mantenere e in un periodo di tempo più lungo è più facile introdurre bug nei test stessi. Per i dettagli guarda qui: http://googletesting.blogspot.com/2008/09/tott-data-driven-traps.html

Anche dal mio punto di vista Unittests sono i più utili quando si sta facendo seria refactoring e non sono sicuro se non avete cambiato la logica in modo sbagliato. Se il test dei dati casuali fallisce dopo questo tipo di modifiche, allora puoi indovinare: è a causa dei dati o delle modifiche?

Tuttavia, penso che potrebbe essere utile (come i test di stress che non sono riproducibili al 100%). Ma se si utilizza un sistema di integrazione continua, non sono sicuro che i test basati sui dati con un'enorme quantità di dati generati casualmente debbano essere inclusi in esso. Preferisco fare una distribuzione separata che periodicamente rende un lotto di di test casuali contemporaneamente (quindi la possibilità di scoprire qualcosa di brutto dovrebbe essere piuttosto alta ogni volta che lo si esegue). Ma è troppo pesante come una parte della normale suite di test.