2012-04-12 14 views
14

Sono un neofita del testing delle unità e PHPUnit, ma di recente ho letto molto sui modelli di progettazione e sui test isolati e ho deciso di rifattorizzare un'applicazione a cui sto lavorando sbarazzarsi di classi statiche, singleton, dipendenze hardcoded e qualsiasi altra cosa definita nell'ambito globale, sperando di renderlo "testabile" e non un rompicapo da mantenere in futuro, dal momento che è pensato per essere un progetto a lungo termine.Esecuzione del test delle unità con dipendenze nidificate e classi di fabbrica

Finora credo di aver capito la teoria del collaudo delle unità, ma mi chiedevo, in uno scenario in cui uno delegasse a gestire dipendenze annidate di oggetti in una fabbrica, come si dovrebbe fare un test unitario su Factory, o è solo ridondante per testarlo? E qual è l'approccio migliore per verificare che la "catena" delle dipendenze funzioni bene in sincronia?

Lasciatemi illustrare le domande. Supponiamo di avere il seguente codice "legacy":

class House { 
    protected $material; 
    protected $door; 
    protected $knob; 

    public function __construct() { 
     $this->door = new Door(); 
     $this->knob = $this->door->getKnob(); 
     $this->material = "stone"; 

     echo "House material: ".$this->material . PHP_EOL . "<br/>"; 
     echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>"; 
     echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>"; 
    } 
} 

class Door { 
    protected $material; 
    protected $knob; 

    public function __construct() { 
     $this->knob = new Knob(); 
     $this->material = "wood"; 
    } 

    public function getKnob() { 
     return $this->knob; 
    } 

    public function getMaterial() { 
     return $this->material; 
    } 

} 

class Knob { 
    protected $material; 

    public function __construct() { 
     $this->material = "metal"; 
    } 

    public function getMaterial() { 
     return $this->material; 
    } 
} 

$house = new House(); 

Questo è (per quanto riguarda la mia comprensione va) un male per il test delle unità, in modo da sostituire le dipendenze hardcoded con DI + una classe di fabbrica:

class House { 
    protected $material; 
    protected $door; 
    protected $knob; 

    public function __construct($door) { 
     $this->door = $door; 
     $this->knob = $this->door->getKnob(); 
     $this->material = "stone"; 

     echo "House material: ".$this->material . PHP_EOL . "<br/>"; 
     echo "Door material: ".$this->door->getMaterial() . PHP_EOL . "<br/>"; 
     echo "Knob material: ".$this->knob->getMaterial() . PHP_EOL . "<br/>"; 
    } 
} 

class Door { 
    protected $material; 
    protected $knob; 

    public function __construct($knob) { 
     $this->knob = $knob; 
     $this->material = "wood"; 
    } 

    public function getKnob() { 
     return $this->knob; 
    } 

    public function getMaterial() { 
     return $this->material; 
    } 

} 

class Knob { 
    protected $material; 

    public function __construct() { 
     $this->material = "metal"; 
    } 

    public function getMaterial() { 
     return $this->material; 
    } 
} 

class HouseFactory { 
    public function create() { 
     $knob = new Knob(); 
     $door = new Door($knob); 
     $house = new House($door); 

     return $house; 
    } 
} 

$houseFactory = new HouseFactory(); 
$house = $houseFactory->create(); 

Ora (e ancora, per quanto ho capito) House, Door e Knob possono essere testati unitamente alle dipendenze fittate. Ma:

1) Cosa succede ora con HouseFactory?

Se uno solo:

  • Non testarlo dal momento che non ha alcuna logica di applicazione pena testare ancora e fabbriche in genere rimanere in quel modo. Supponiamo che se i test indipendenti per House, Door & Knob passino la Factory dovrebbe andare bene.
  • Riforma la factory in qualche modo, cioè utilizzando le funzioni all'interno della classe per ottenere ogni istanza in modo tale che si possano sovrascrivere queste funzioni tramite PHPUnit per restituire oggetti mock, nel caso ci sia qualche logica in più nella classe che potrebbe usare alcuni test in futuro.

2) È possibile impostare test che si basano su più dipendenze (non prese in giro) contemporaneamente? Capisco che tecnicamente non sia un test unitario (test di integrazione forse?) Ma immagino che sia ancora perfettamente utilizzabile con PHPUnit? Considerato l'esempio sopra, vorrei essere in grado di impostare un test che non solo collauda House, Door, Knob e HouseFactory in isolamento, ma anche i risultati dell'interazione degli oggetti reali tra loro, forse con alcuni dei loro funzioni irrisolte, come quelle che si occupano di dati. PHPUnit è una cattiva scelta per questo tipo di test?

Grazie in anticipo per il vostro tempo. Mi rendo conto che alcune delle ipotesi che sto facendo potrebbero non essere corrette, dal momento che ovviamente non sono un esperto in materia; le correzioni sono benvenute e apprezzate.

risposta

4

La fabbrica è come la parola chiave new. Testate la parola chiave new? No, tu prova se riesci a costruire una classe. Ma questo è indipendente dalla fabbrica stessa e da una parte dell'unità, quindi fa già parte dei test dell'unità.

2) viene chiamato test di integrazione. E puoi farlo anche con PHPUnit.


Modifica - Come c'era qualche discussione nei commenti:

Per quanto riguarda il test delle unità è interessato, si potrebbe unità di testare la vostra fabbrica che lo fa per quello che è per: restituire un tipo concreto, un tipo o qualsiasi tipo.

Non c'è niente di sbagliato in questo, tuttavia normalmente non è necessario in quanto i costruttori dei tipi restituiti sono già in fase di test unitari e il test è davvero banale e solo il controllo dei dati che odora di test di integrazione. Anche i tipi che hanno quel tipo dalla dipendenza di fabbrica (e che sono sotto il test unitario) renderanno la compilazione/esecuzione fallita se la dipendenza non può essere fornita. Quindi tutto ciò per cui la fabbrica è pronta, è già testato, anche da entrambe le parti. E se la fabbrica non viene consumata, allora non è necessario testarla.

Vi suggerisco di creare una fabbrica di puro stile TDD, in modo di controllare la validità formulare l'uso e quindi si otterrà una sensibilità per questo. Potresti voler testare altri aspetti delle tue classi di fabbrica, ma probabilmente questo è più di integrazione rispetto ai test di unità.

E io non volevo dare l'impressione che altro delle tue unità dovrebbe in realtà sono hardcoded chiamate al metodo di fabbrica di creare (s) invece di ottenere la dipendenza iniettato. Siccome non dovresti usare new all'interno delle tue unità, non dovresti usare neanche Factory::create. Simile a new, il nome della classe (Factory) è codificato a macchina, non iniettato. È una dipendenza nascosta allora. Ma le dipendenze non dovrebbero essere nascoste; ma reso visibile.

+0

Il factory non è come la parola chiave 'new'. Può essere testato. Cosa dovrebbe fare 'HouseFactory :: create' do? Crea e * restituisce * una 'Casa'. Puoi e dovresti testarlo, sia con un test unitario che con un'asserzione per lo meno. L'esempio OP è semplice, ma se il tuo metodo ha qualche logica in esso, dovrebbe essere testato. – netcoder

+0

Inoltre, non è possibile testare la parola chiave 'new' in PHP (perché non è sovraccarico), ma è possibile sovraccaricarlo in C++ ad esempio, e si dovrebbe testarlo se lo si fa. – netcoder

+0

Poiché il costruttore di 'house' è già stato testato unitamente (come per TDD), non c'è molto bisogno di testare normalmente il metodo factory. Puoi testare tutto, la domanda è se ha senso ed è davvero necessaria. I test di integrazione possono coprire l'utilizzo della fabbrica e quindi salvarli se la fabbrica non è ciò per cui è stata realizzata. – hakre

0

È possibile testarlo con ereditarietà.

Basta estendere casa con un FakeHouse per i test, e quindi controllare $ materiale, $ porta e $ manopola ecc se hanno cambiato o no dopo la prova.

Problemi correlati