2009-06-30 5 views
31

Quindi ho una classe di fabbrica e sto cercando di capire cosa dovrebbero fare i test unitari. Da questo question ho potuto verificare che l'interfaccia restituita sia di un particolare tipo concreto che mi aspetterei.Metodi di fabbrica di test unitari che hanno una classe concreta come tipo di ritorno

Che cosa devo verificare se la fabbrica restituisce tipi concreti (perché non è necessario - al momento - per le interfacce da utilizzare)? Attualmente sto facendo qualcosa di simile al seguente:

[Test] 
public void CreateSomeClassWithDependencies() 
{ 
    // m_factory is instantiated in the SetUp method 
    var someClass = m_factory.CreateSomeClassWithDependencies(); 

    Assert.IsNotNull(someClass); 
} 

Il problema di questo è che l'Assert.IsNotNull sembra un po 'ridondante.

Inoltre, il mio metodo factory potrebbe essere l'impostazione dei dipendenze di quella particolare classe in questo modo:

public SomeClass CreateSomeClassWithDependencies() 
{ 
    return new SomeClass(CreateADependency(), CreateAnotherDependency(), 
         CreateAThirdDependency()); 
} 

e voglio fare in modo che il mio metodo factory imposta tutte queste dipendenze correttamente. Non c'è altro modo per fare questo per rendere quelle proprietà di dipendenze public/internal che poi controllo nel test dell'unità? (Non sono un grande fan di modificare i soggetti del test per adattarli ai test)

Modifica: in risposta alla domanda di Robert Harvey, sto usando NUnit come framework di test delle unità (ma non avrei pensato che farebbe troppa differenza)

+0

Quale framework di test stai usando? –

+0

Alcuni framework di test richiedono che le classi siano virtuali in modo che il framework di testing possa ereditarle. Alcuni no. Differenza enorme. –

risposta

32

Spesso non c'è niente di sbagliato nella creazione di proprietà pubbliche che possono essere utilizzate per test basati sullo stato. Sì: è il codice che hai creato per abilitare uno scenario di test, ma danneggia la tua API? È ipotizzabile che altri clienti trovino la stessa proprietà utile in seguito?

C'è una linea sottile tra codice specifico per test e Design guidato da test. Non dovremmo introdurre un codice che non ha altro potenziale se non quello di soddisfare un requisito di test, ma è piuttosto giusto introdurre un nuovo codice che segua i principi di progettazione generalmente accettati. Lasciamo che l'unità di test nostro design - è per questo che chiamiamo TDD :)

L'aggiunta di uno o più oggetti da una classe per dare all'utente una maggiore possibilità di ispezionare quella classe è, a mio parere, spesso un ragionevole cosa da fare, quindi non penso che dovresti licenziare l'introduzione di tali proprietà.

A parte questo, ho seconda risposta di Nader :)

+1

+1 per "let testing guidare il nostro design". L'uso delle proprietà per esporre le dipendenze aiuta sia a configurare le dipendenze, sia a renderle evidenti. Entrambe sono buone cose. –

+0

@nader: Ben messo - era quello che stavo cercando di dire :) –

+0

+1 Così spesso ho bisogno di fare qualcosa di una proprietà pubblica in modo che possa testare l'unità solo per scoprire che ne avevo bisogno più tardi nel codice e altre persone anche bisogno di esso. – uriDium

3

Quello che facciamo è creare le dipendenze con le fabbriche, e usiamo un framework di iniezione dipendente per sostituire le fabbriche fittizie con quelle reali quando il test è in esecuzione. Quindi stabilimmo le aspettative appropriate su quelle fabbriche finte.

3

È sempre possibile controllare le cose con la riflessione. Non è necessario esporre qualcosa solo per i test unitari. Trovo abbastanza raro che io debba entrare in contatto con la riflessione e potrebbe essere un segno di cattiva progettazione.

Guardando il vostro codice di esempio, sì Assert non nullo sembra ridondante, a seconda del modo in cui avete progettato la vostra fabbrica, alcuni restituiranno oggetti nulli dalla fabbrica piuttosto che eccezioni.

23

Se la fabbrica restituisce tipi di calcestruzzo e si garantisce che la propria fabbrica restituisca sempre un tipo concreto, e non null, quindi no, non c'è troppo molto valore nel test. Ti permette di assicurarti, nel tempo, che questa aspettativa non sia violata e che non vengano lanciate eccezioni come le eccezioni.

Questo tipo di test fa semplicemente in modo che, come si apportano modifiche in futuro, il comportamento di fabbrica non cambierà senza che tu lo sappia.

Se la lingua lo supporta, per le dipendenze è possibile utilizzare la riflessione. Questo non è sempre il modo più facile da mantenere e accoppia i test molto strettamente alla tua implementazione. Devi decidere se è accettabile. Questo approccio tende ad essere molto fragile.

Ma sembra proprio che tu stia cercando di separare quali classi sono costruite, da come sono chiamati i costruttori. Potrebbe essere meglio usare un framework DI per ottenere quel tipo di flessibilità.

Entro il new -su tutti i tipi di cui hai bisogno, non ti dai molte cuciture (una cucitura è un posto dove puoi modificare il comportamento nel tuo programma senza modificare in quel posto) con cui lavorare.

Con l'esempio dato, tuttavia, è possibile ottenere una classe dalla fabbrica.Quindi eseguire l'override/simulazione CreateADependency(), CreateAnotherDependency() e CreateAThirdDependency(). Ora quando chiami CreateSomeClassWithDependencies(), sei in grado di senso se sono state create o meno le dipendenze corrette.

Nota: la definizione di "cucitura" deriva dal libro di Michael Feather "Lavorare in modo efficace con il codice legacy". Contiene esempi di molte tecniche per aggiungere testabilità al codice non testato. Potresti trovarlo molto utile.

+1

Mi piace la risposta e quel libro è un dio inviato se si ha a che fare con il codice legacy o vicino a legacy (.net 1.1) senza test. Buono anche se si è in una squadra in cui non è stato fatto precedentemente il test delle unità e si dispone di un sacco di codice brownfield per il quale si desidera ottenere un'unità testata che il libro sia super utile. – ElvisLives

0

quanto ho capito si vogliono testare che le dipendenze sono costruiti in modo corretto e passati alla nuova istanza?

Se non ero in grado di utilizzare un quadro di come Google Guice, probabilmente lo fanno qualcosa di simile (qui utilizzando JMock e Hamcrest):

@Test 
public void CreateSomeClassWithDependencies() 
{ 
    dependencyFactory = context.mock(DependencyFactory.class); 
    classAFactory = context.mock(ClassAFactory.class); 

    myDependency0 = context.mock(MyDependency0.class); 
    myDependency1 = context.mock(MyDependency1.class); 
    myDependency2 = context.mock(MyDependency2.class); 
    myClassA = context.mock(ClassA.class); 

    context.checking(new Expectations(){{ 
     oneOf(dependencyFactory).createDependency0(); will(returnValue(myDependency0)); 
     oneOf(dependencyFactory).createDependency1(); will(returnValue(myDependency1)); 
     oneOf(dependencyFactory).createDependency2(); will(returnValue(myDependency2)); 

     oneOf(classAFactory).createClassA(myDependency0, myDependency1, myDependency2); 
     will(returnValue(myClassA)); 
    }}); 

    builder = new ClassABuilder(dependencyFactory, classAFactory); 

    assertThat(builder.make(), equalTo(myClassA)); 
} 

(se non si può deridere ClasseA è possibile assegnare un versione non-mock a myClassA using new)