È una buona idea avere metodi di test separati per ogni passaggio?

10

Sto testando un'API REST. Diciamo che restituisce una struttura JSON. Qual è l'approccio migliore per testare il server? Ogni passaggio di prova può avere successo solo se tutti i precedenti hanno avuto esito positivo.

Struttura A: prova tutto in una volta

- Test method 1:
    - make server request
    - assert http response code was 200
    - assert returned file is not empty
    - assert returned file has valid JSON syntax
    - assert returned JSON contains key X

Questa sembra essere la soluzione migliore.

I vantaggi:

  • Solo una richiesta del server
  • Sto testando il comportamento nel suo insieme "Il server restituisce un JSON con la chiave X?"

Struttura B: aggiungere gradualmente asserzioni a ciascun test

 - Test method 1:
     - make server request
     - assert http response code was 200
 - Test method 2:
     - make server request
     - assert returned file is not empty
 - Test method 3:
     - make server request
     - assert returned file has valid JSON syntax
 - Test method 4:
     - make server request
     - assert returned JSON contains key X

Questo è il modo in cui ho iniziato a farlo ed ero convinto che questa dovrebbe essere la strada da percorrere perché ogni metodo testa solo una sola cosa che crea una separazione migliore. Ma ora penso che, dal momento che questi non sono test unitari, la mia separazione non è appropriata e dovrei testare il comportamento nel suo insieme.

Struttura C: effettuare una richiesta una volta ed eseguire metodi di test separati sulla risposta in cache

- make server request and cache it (allow read-only access)

 - Test method 1:
     - assert http response code was 200 on cached server request
 - Test method 2:
     - assert returned file is not empty on cached server request
 - Test method 3:
     - assert returned file has valid JSON syntax on cached server request
 - Test method 4:
     - assert returned JSON contains key X on cached server request

I vantaggi:

  • Nessuna richiesta server costosa
  • Ancora ha i metodi di test con asserimento semplice

Qual è la struttura di test più sensata da usare?

    
posta mrplow 29.09.2016 - 17:41
fonte

4 risposte

3

Le migliori pratiche hanno sempre uno scopo, una ragione dietro di esse. È sempre una buona idea considerare questi motivi nel tuo design, specialmente quando stai cercando di decidere come e quanto sia difficile seguire queste best practice.

In questo caso, il ragionamento principale alla base del test di ogni singolo test è che se la prima cosa fallisce, la seconda non verrà testata. Dal momento che troppi opinion maker sembrano trovare il merito di rompere tutto fino al più piccolo bit possibile e avvolgendo ogni cosa nel modo più gonfio possibile, questo ha dato origine all'idea che ogni test dovrebbe contenere un singolo assert.

Non seguirlo ciecamente. Anche se ogni test dovesse testare una cosa, dovresti comunque riflettere su quanto grande o piccola dovrebbe essere ogni "cosa", e per farlo devi tenere a mente perché vuoi che ogni test venga prova una cosa - per assicurarti che un bug nella prima cosa non lasci la seconda cosa non testata.

Quindi, è necessario chiedersi: "ho davvero bisogno di questa garanzia qui?"

Diciamo che c'è un bug nel primo caso di test - il codice di risposta HTTP non è 200 . Quindi inizi a hackerare il codice, capisci perché non hai ricevuto il codice di risposta che dovresti avere e risolvi il problema. E ora cosa?

  • Se esegui di nuovo il test manualmente, per verificare che la tua soluzione risolva il problema, dovresti imbatterti in qualsiasi altro problema nascosto dal primo errore.
  • Se non lo esegui manualmente (forse perché impiega troppo tempo?) e ti basta spingere la tua correzione in attesa che il server di test automatico esegua tutto, allora potresti voler inserire diversi asser in diversi test. I cicli in questo caso sono molto lunghi, quindi vale la pena di fare lo sforzo di scoprire il maggior numero di errori in ogni ciclo.

Ci sono poche altre cose da considerare:

Dipendenze delle asserzioni

So che i test che hai descritto sono solo un esempio ei tuoi test effettivi sono probabilmente più complicati, quindi quello che sto per dire potrebbe non essere valido con la stessa forza nei test reali, ma potrebbe essere ancora un po ' efficace in modo da poterlo considerare.

Se si dispone di un servizio REST (o qualsiasi altro protocollo HTTP) che restituisce risposte in formato JSON, di solito si scrive una semplice classe client che consente di utilizzare i metodi REST come metodi regolari che restituiscono oggetti regolari. Supponendo che il cliente abbia test separati per assicurarsi che funzioni, avrei abbandonato i primi 3 assert e ne avrei conservati solo 4!

Perché?

  • La prima affermazione è ridondante - la classe client dovrebbe generare un'eccezione se il codice di risposta HTTP non è 200.
  • La seconda asserzione è ridondante - se la risposta è vuota, l'oggetto risultato sarà nullo o qualche altra rappresentazione di un oggetto vuoto, e non avrai nessun posto dove mettere la chiave X.
  • Il terzo assert è ridondante - se il JSON non è valido, ottieni un'eccezione quando provi ad analizzarlo.

Quindi non è necessario eseguire tutti questi test: basta eseguire il quarto test e, se uno qualsiasi dei bug che i primi tre cercano di rilevare, il test fallirà con un'eccezione appropriata prima ancora di ottenere l'asserzione effettiva.

Come si desidera ricevere i report?

Supponiamo di non ricevere email da un server di prova, ma invece il reparto QA esegue i test e notifica i test non riusciti.

Jack del QA bussa alla tua porta. Dice che il primo metodo di test non è riuscito e che il metodo REST ha restituito un codice di risposta errato. Lo ringrazi e inizi a cercare la causa principale.

Poi arriva Jen dal QA e dice che il terzo metodo di test non è riuscito - il metodo REST non ha restituito un JSON valido nel corpo della risposta. Le dici che stai già osservando quel metodo e credi che la stessa cosa che ha causato il ritorno di un brutto codice di uscita ha anche causato il ritorno di qualcosa che non è un JSON valido, e assomiglia più a una traccia dello stack di eccezioni.

Ritorni a lavorare, ma poi arriva Jim dal QA, dicendo che il quarto metodo di test è fallito e che non c'è la chiave X nella risposta ...

Non puoi nemmeno cercare il motivo perché è difficile guardare il codice quando non hai uno schermo di computer. Se Jim fosse stato abbastanza veloce avrebbe potuto schivarlo in tempo ...

Le e-mail dal server di test sono più facili da respingere, ma ancora - preferiresti semplicemente ricevere una notifica UNA VOLTA che qualcosa non funziona con il metodo di prova e guardare i log di test rilevanti da te ?

    
risposta data 30.09.2016 - 17:37
fonte
3

Se si può tranquillamente supporre che fare una richiesta server con gli stessi parametri si comporti sempre nello stesso modo, il metodo B è quasi inutile - perché dovresti chiamare quattro volte lo stesso metodo per ottenere gli stessi dati di risposta quattro volte quando una chiamata è abbastanza?

E se non puoi assumerlo con sicurezza e vuoi farlo diventare parte del test, potresti essere più adatto a eseguire il test A più volte.

L'unica situazione ipotetica che vedo dove B potrebbe avere un vantaggio è quando il tuo framework di test consente di attivare e disattivare solo i metodi di test espliciti e ti aspetti la necessità di farlo per i singoli passaggi del test.

L'alternativa C sembra combinare A con l'unico vantaggio che ho citato sopra per B. Se il tuo framework di test consente al tuo codice di essere strutturato facilmente in questo modo, senza molto overhead sopra B, questo è un approccio fattibile. Tuttavia, questo aggiunge un po 'di complessità addizionale ad A, quindi lo userei solo se mai volessi attivare e disattivare i singoli test, altrimenti applicare il principio YAGNI e attenersi alla soluzione più semplice (A).

TLDR: inizia con A se sei sicuro di voler sempre eseguire tutte le asserzioni in un test, refactoring in C se noti che devi avere un controllo più semplice dall'esterno sui singoli asser.

    
risposta data 29.09.2016 - 19:53
fonte
0

Come qualsiasi codice, evita l'ottimizzazione prematura. Prima scrivi i tuoi test in modo che siano semplici da leggere e semplici da mantenere. Quando i test iniziano a diventare troppo lenti, ottimali. Nel tuo esempio abbastanza semplice, A e B saranno entrambi facili da leggere e conservare, quindi scegli quello che vuoi finché le cose non diventano troppo lente (struttura B) o troppo complicate (struttura A).

Se il tuo server è senza stato, puoi ottimizzare confrontando la risposta effettiva con una risposta attesa per verificare l'intero messaggio in un colpo solo. Ovviamente ciò sarà a scapito della leggibilità.

Se il tuo server ha lo stato ed è necessario effettuare più chiamate api lente per ottenere il server in uno stato per il test, allora devi adottare un approccio diverso o i test potrebbero richiedere alcuni minuti. Ad esempio, è possibile eseguire un aggiornamento del database per inserire dati in un database di test in modo da poter ottenere rapidamente un oggetto in uno stato appropriato per il test. Il test è veloce e leggibile ma più difficile da mantenere. In alternativa, potresti essere in grado di scrivere una facciata davanti all'api in modo che le chiamate api multiple diventino chiamate api singole che corrispondono più strettamente al processo aziendale che stai testando.

    
risposta data 29.09.2016 - 23:55
fonte
0

I test non dovrebbero condividere le cose - partendo da zero si evita l'influenza di un test su un altro. Questo ti rende anche in grado di eseguire test in ordine casuale.
Quindi il modo non dovrebbe essere accettato.

Quando scrivi un codice (o forse addirittura crei qualcos'altro), chiedi sempre a te stesso: "perché esiste una tale pratica?"
Perché diciamo che dovrebbero esserci diversi test per tutto?

Ci sono due casi in cui ti serve:

  1. quando non puoi fare affidamento sul "ogni passaggio di prova può avere successo solo se tutti i precedenti hanno avuto esito positivo"
  2. quando i tuoi test non hanno messaggi descrittivi di asserzione

Ci sono due ragioni per cui affronti questi casi:

  1. "ogni passaggio di prova può avere successo solo se tutti i precedenti hanno avuto successo" non è realmente applicabile alla funzione del prodotto
  2. non hai abbastanza conoscenza del prodotto a causa della mancanza di esperienza o del tempo, o della stragrande complessità del prodotto

Se per qualche motivo non puoi dichiarare almeno uno di questi motivi per aver posto solo ciecamente la struttura B .

Altrimenti (spero che tu arrivi qui) scegli A .

Puoi anche fare questa domanda su Assicurazione della qualità del software & Test sito Stackexchange.

    
risposta data 05.10.2016 - 18:42
fonte

Leggi altre domande sui tag