Ho testato l'unità della mia classe, ora come posso iniziare con un test di integrazione?


19

Ho scritto una classe che gestisce i destinatari in un elenco MailChimp, chiamato MailChimpRecipient. Utilizza la classe MCAPI, che è un wrapper API di terze parti.

http://apidocs.mailchimp.com/api/1.3/ http://apidocs.mailchimp.com/api/downloads/

Passo l'oggetto MCAPI nel costruttore dell'oggetto MailChimpRecipient, quindi ho scritto test unitari usando PHPUnit che testano tutta la logica della mia classe (non sto testando la classe MCAPI). Ho una copertura del codice del 100% e tutti i test superano. Questo viene fatto deridendo e stubbing l'oggetto MCAPI.

Il mio prossimo passo è stato scrivere un test di integrazione, usando anche PHPUnit, dove avrei costruito l'apparecchiatura MailChimpRecipient usando un vero oggetto MCAPI, impostato per usare un vero elenco MailChimp.

Ho scritto quello che penso sia un test di integrazione, che fondamentalmente esegue test sull'interfaccia pubblica dell'oggetto, come:

public function testAddedRecipientCanBeFound()
{
    $emailAddress = 'fred@fredsdomain.com';
    $forename = 'Fred';
    $surname = 'Smith';

    // First, delete the email address if it is already on the list
    $oldRecipient = $this->createRecipient();
    if($oldRecipient->find($emailAddress))
    {
        $oldRecipient->delete();
    }
    unset($oldRecipient);

    // Add the recipient using the test data
    $newRecipient = $this->createRecipient();
    $newRecipient->setForename($forename);
    $newRecipient->setSurname($surname);
    $newRecipient->setEmailAddress($emailAddress);
    $newRecipient->add();
    unset($newRecipient);

    // Assert that the recipient can be found using the same email address
    $this->assertTrue($this->_recipient->find($emailAddress));
}

Il test di "integrazione" non verifica nessuno degli interni della classe, ma si assicura solo che, dato un vero oggetto MCAPI, si comporti come pubblicizzato.

È corretto? È questo il modo migliore per eseguire un test di intergazione? Dopotutto, gli interni sono stati testati con un test unitario. Ho ragione nel pensare che il test di integrazione sia lì per verificare che funzioni davvero, in base al modo in cui viene pubblicizzato il suo comportamento?

Per fare un ulteriore passo avanti, la classe MailChimpRecipient implementa un'interfaccia, che sarà implementata anche da altre classi. L'idea è quella di utilizzare una fabbrica per passare diversi tipi di oggetti destinatario della mailing list al mio codice, che fanno tutti la stessa cosa, anche se usando diversi provider di mailing list. Dato che i miei test di integrazione testano quell'interfaccia, che ne dici di usarla per tutte le classi che implementano l'interfaccia? Quindi, in futuro, se progetto una nuova classe da utilizzare in modo intercambiabile, posso eseguire lo stesso test di integrazione prima di inserirlo in un progetto.

Sembra ragionevole? I test unitari testano gli interni di un oggetto, i test di intergrazione assicurano che si comporti come pubblicizzato?


4
Penso che tu abbia troppa logica nel tuo test. Esegui molto codice fino a quando non fai l'asserzione. Probabilmente si desidera verificare prima l'eliminazione di un destinatario. Ma questo non risponde alla tua domanda, solo un commento.
Hacre,

1
Bene, dovresti usare la setUpfunzione per stabilire i motivi per eseguire i test. Se l'input non è definito, non puoi davvero testarlo. L'input deve essere preciso, rigoroso e sempre lo stesso. Se non è soddisfatta una condizione preliminare di un test, saltare invece il test. Quindi analizzare perché salta e se è necessario aggiungere ulteriori test e / o il setUpnon è fatto correttamente.
Hakre,

1
Inoltre, non codificare i valori dei test all'interno di un test proprio, ma probabilmente rendere quei membri della classe in modo che possano essere condivisi tra i test (e modificati in un punto centrale) o utilizzare DataProvider(questa è una funzione che offre input come parametri per un test).
Hakre,

1
Inserisci nel significato di tutto ciò su cui opera la tua funzione di test. Quando testate di aggiungere un destinatario e volete assicurarvi che non esista già, dovreste almeno affermare la cancellazione nel caso in cui si avvii. Altrimenti non è garantito che il prerequisito del test sia testabile.
Hakre,

1
+1 per una buona domanda, ma anche votato per migrare ai programmatori. Sembra che appartengano alle domande sulle strategie di test
GordonM,

Risposte:


17

Durante il test del codice, è necessario prestare attenzione a tre aree:

  • Test dello scenario
  • Test funzionali
  • Test unitari

Normalmente la quantità di test che hai in ogni categoria avrebbe la forma di una piramide, il che significa molti test unitari nella parte inferiore, alcuni test funzionali nel mezzo e solo alcuni test di scenario.

Con un unit test prendi in giro tutto ciò che la classe sotto test usa e lo collaudi in puro isolamento (ecco perché è importante assicurarsi che all'interno della tua classe recuperi tutte le dipendenze attraverso l'iniezione in modo che possano essere sostituite sotto test).

Con i test unitari si verificano tutte le possibilità, quindi non solo il "percorso felice", ma anche tutte le condizioni di errore.

Se sei completamente sicuro che tutte le tue unità funzionino in modo isolato, scrivi un paio di test (test funzionali) per assicurarti che anche le unità funzionino quando combinate. Quindi si scrive un test di scenario, che verifica il cablaggio tra tutti i moduli funzionali.

Ad esempio, supponiamo che stai testando un'auto.

Potresti assemblare l'intera macchina e come pilota controllare ogni possibile condizione, ma sarebbe davvero difficile da fare.

Invece testeresti una piccola parte del motore con tutte le possibilità (unit test)

Quindi si testa l'intero motore (separato dall'auto) che sarebbe un test funzionale.

Come ultimo test, inserisci la chiave, avvia la macchina e la porti nel parcheggio. Se funziona, allora sai che tutte le parti (batteria, carburante, motore, ...) sono collegate e dato che le hai provate isolatamente puoi essere abbastanza sicuro che l'intera auto funzioni correttamente.

Quindi, nel tuo caso, hai testato tutte le condizioni di errore e il percorso felice nel test dell'unità e sai che devi solo eseguire un test end-to-end con i "componenti reali" per verificare se il cablaggio è corretto.

Alcuni altri punti,

  • Evita la logica condizionale nel test unitario. Se devi ripulire, stai usando una sorta di stato globale e i test possono improvvisamente influenzarsi a vicenda.
  • Non specificare alcun dato non pertinente per il test. Se cambiassi nome o cognome, il test fallirebbe? Probabilmente non perché è l'indirizzo email che è importante, ma perché lo menzioni esplicitamente nel tuo test, non posso esserne sicuro. Prova a guardare il Builder Pattern per creare i tuoi dati di test e renderlo esplicito ciò che è veramente importante.

Grazie, ciò conferma molto di ciò che pensavo. Giusto per chiarire: questo NON è un test unitario. Ho già scritto un unit test, che testa l'oggetto in completo isolamento e ha una copertura del codice del 100% dell'oggetto. Questo doveva essere un test di integrazione, per assicurarsi che funzionasse quando iniettavo un vero oggetto MCAPI in esso. Devo solo eliminare tutti i destinatari aggiunti all'elenco: è tutto ciò che serve per la pulizia ed è implementato per garantire che nessuno dei test si influenzi reciprocamente. Cosa suggeriresti invece?

1
Si! Ho capito che hai già fatto i test unitari. L'oggetto MCAPI tiene traccia dei destinatari ed è la pulizia che devi fare? Se si tratta del "problema" di terze parti, non è possibile eseguire alcuna operazione in un test di integrazione. Se l'altro tieni traccia dell'elenco, dovresti assicurarti di evitare dati globali (e singoli) per assicurarti che i test non si influenzino a vicenda. In un mondo perfetto, ripulire le cose quando un test inizia / finisce, indica un difetto di progettazione ma nel mondo reale, non puoi sempre evitarlo.
Wouter de Kort,

1
Aggiungerei che il test dello scenario non è probabilmente qualcosa a cui PHPUnit è adatto. Yu potrebbe voler esaminare alcuni strumenti che è possibile eseguire in un browser come Selenium o uno strumento in grado di simulare un browser, come jMeter.
GordonM,

Grazie ragazzi! C'è sicuramente molto da imparare quando si tratta di scrivere un buon codice testabile, non c'è. Mi sono ordinato una copia di questo libro: amazon.co.uk/… . Spero che tutto quello che hai detto abbia un po 'più senso dopo averlo letto. @Wouter, sto solo cancellando un destinatario perché il test avrebbe causato l'aggiunta di un indirizzo e-mail all'elenco. Lo sto eliminando in modo che l'elenco non sia interessato da quel test.

1
@LewisBassett Non sono uno sviluppatore di Php ma i modelli di test xUnit ( amazon.com/xUnit-Test-Patterns-Refactoring-Code/dp/0131495054 ) sono sicuramente una buona lettura. Anche gli articoli su misko.hevery.com/code-reviewers-guide sono davvero interessanti.
Wouter de Kort,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.