2011-12-23 1 views
12

Sto scrivendo una classe che erediterà un'interfaccia. Il codice client verrà scritto per quell'interfaccia e la classe scritta per supportarlo. L'idea è che in seguito scriverò altre classi per quell'interfaccia e gli oggetti di queste due classi differenti dovrebbero essere completamente intercambiabili. Piuttosto che scrivere una classe di test per quella prima lezione, voglio scriverne una per l'interfaccia.PHPUnit - Scrittura di una classe di test per un'interfaccia e test degli oggetti utilizzando una fabbrica

Il mio piano era scrivere una classe di test che avrebbe preso un oggetto factory per il costruttore (dependency injection) e utilizzare la factory per creare nuove istanze della classe sottoposta a test.

In questo modo, se volessi testare ClassA, potrei passare un oggetto ClassAFactory al costruttore della classe di test e, se volessi testare ClassB, passerei un oggetto ClassBFactory. Entrambe le classi sono progettate per essere intercambiabili e dato che solo i metodi pubblici dovrebbero essere testati, questo sembra l'ideale.

Ma che dire del test del costruttore? Farei meglio a scrivere una classe di test astratta e a impiantare test del costruttore in classi che ereditano la classe di test astratta (diverse classi possono essere istanziate in modo diverso)?

Se ho usato la prima idea, credo che avrei avuto una classe di test per ogni classe in fase di sperimentazione, come:

class ClassATest extends [PHPUnit test case] 
{ 
    $myFactory = new ClassAFactory(); 
    $myTest = new ClassTest($myFactory); 

    $myTest->test1(); 
    $myTest->test2(); 
    //etc. 
} 

Qual è il modo migliore per andare su questo? Voglio fare un test generale, in modo che quando scrivo nuove classi per implementare l'interfaccia comune, posso semplicemente mettere un oggetto degli stessi test usati per gli altri. Ma visto che le diverse classi avrebbero costruttori diversi, forse scrivere una classe di test astratta ed estenderla per ogni nuovo oggetto sarebbe meglio? Cosa ne pensi?

risposta

6

Penso che sia necessario riconsiderare il piano. Non è possibile testare un'interfaccia e c'è una buona ragione per cui - le interfacce definiscono semplicemente l'API e non la funzionalità, la funzionalità di test dei test. Permettetemi di darvi un esempio che potrebbe aiutare. Supponiamo tu abbia un'interfaccia "di messaggistica". Quindi implementa un EmailMessager e un SMSMessager. Ora devi testarli separatamente come con l'EmailMessager di cui hai bisogno per assicurarti che stia facendo la sua roba, magari convalidando il destinatario (un indirizzo email) e possibilmente delegando l'invio a una classe email ecc. Ovviamente un messaggio SMS sarebbe diverso.

+1

In questo specifico esempio, le classi rappresenteranno i destinatari di una mailing list. Anche se avranno diversi modi di fare il loro lavoro, prenderanno gli stessi dati da un cliente e restituiranno gli stessi risultati - completamente intercambiabili. Mi sembra che se gli oggetti di classi diverse che usano quell'interfaccia sono intercambiabili all'interno del codice client, dovrebbero essere intercambiabili anche all'interno del codice di test. Quindi quando dico di voler testare l'interfaccia, intendo dire che voglio scrivere un test su quell'interfaccia, per testare una classe che usa quell'interfaccia. Ha senso per te? –

+1

No, non ha ancora senso. Non è così che capisco un'interfaccia per funzionare. Potresti desiderare una classe base astratta piuttosto che un'interfaccia per implementare il codice comune. Non sono sicuro di cosa ci sia di diverso nelle tue lezioni oltre ai dati. – liquorvicar

+0

Diciamo sulla classe, MailChimpRecipient utilizza l'interfaccia MailingListRecipient: il client codificato aggiunge qualcuno alla lista impostando l'indirizzo email e le proprietà del nome dell'oggetto destinatario, quindi chiamando un metodo per aggiungerli. Ora, questo metodo per la classe MailChimp utilizzerà l'API MailChimp per aggiungerli. Dire in futuro, voglio ospitare la mia mailing list o usare un altro provider, potrei cambiare la classe con un'altra classe che usa l'interfaccia MailingListRecipient. Quella classe potrebbe fare qualcosa di completamente diverso per aggiungere il destinatario, ma richiede comunque gli stessi dati. –

6

È possibile creare un test case astratto con tutti i metodi di test utilizzando una proprietà che ciascuna sottoclasse imposterà senza richiedere una factory. Può testare le qualità fisse che tutte le implementazioni devono possedere.

abstract class IAdderTestCase extends PFTC 
{ 
    function testAdd() { 
     self::assertEquals(5, $this->fixture->add(2, 3)); 
    } 
    ... 
} 

class BasicAdderTest extends IAdderTestCase 
{ 
    function setUp() { 
     $this->fixture = new BasicAdder(); 
    } 
} 

PHPUnit chiamerà setUp() prima di ogni metodo di prova. Dovrebbe chiamare tutti i metodi di test ereditati per ciascuna sottoclasse di calcestruzzo più eventuali altri, ad es. per testare i costruttori.

+0

Grazie per quello! Questo sembra il modo migliore per andare, in quanto posso quindi cambiare il metodo setUp per ogni sottoclasse diversa. –