Domanda

Tempo fa ho letto il I mock non sono stub articolo di Martin Fowler e devo ammettere che ho un po' paura delle dipendenze esterne per quanto riguarda la complessità aggiuntiva, quindi vorrei chiedere:

Qual è il metodo migliore da utilizzare durante i test unitari?

È meglio utilizzare sempre un framework simulato per simulare automaticamente le dipendenze del metodo da testare o preferiresti utilizzare meccanismi più semplici come ad esempio gli stub di test?

È stato utile?

Soluzione

Come dice il mantra "Scegli la cosa più semplice che possa funzionare".

  1. Se le lezioni false possono portare a termine il lavoro, seguile.
  2. Se hai bisogno di un'interfaccia con più metodi per essere deriso, scegli un framework fittizio.

Evita di usare finzioni Sempre perché rendono fragili i test.I tuoi test ora hanno una conoscenza complessa dei metodi chiamati dall'implementazione, se l'interfaccia derisa o l'implementazione cambia...i tuoi test si interrompono.Questo è un male perché passerai più tempo a eseguire i test invece di far eseguire semplicemente il tuo SUT. I test non dovrebbero essere inopportunamente intimi con l'implementazione.
Quindi usa il tuo miglior giudizio..Preferisco le prese in giro quando mi aiutano a risparmiarmi la scrittura e l'aggiornamento di una classe falsa con n>>3 metodi.

Aggiornamento Epilogo/Deliberazione:
(Grazie a Toran Billups per l'esempio di un test mockico.Vedi sotto)
Ciao Doug, beh penso che siamo trascesi in un'altra guerra santa: TDD classici contro TDD simulati.Penso di appartenere al primo.

  • Se sono su test#101 Test_ExportProductList e trovo che devo aggiungere un nuovo parametro a IProductService.GetProducts().Lo faccio per ottenere questo test verde.Utilizzo uno strumento di refactoring per aggiornare tutti gli altri riferimenti.Ora trovo che tutti i test mockici che chiamano questo membro ora esplodano.Poi devo tornare indietro e aggiornare tutti questi test: una perdita di tempo.Perché ShouldPopulateProductsListOnViewLoadWhenPostBackIsFalse ha avuto esito negativo?È stato perché il codice è rotto?Piuttosto i test sono interrotti.Sono favorevole a un fallimento del test = 1 posto da correggere.La frequenza beffarda va contro questo.Sarebbero migliori gli stub?Se avessi un fake_class.GetProducts()..sicuro Un posto dove cambiare invece di un intervento chirurgico con il fucile su più chiamate Expect.Alla fine è una questione di stile..se avessi un metodo di utilità comune MockHelper.SetupExpectForGetProducts() - sarebbe anche sufficiente..ma vedrai che questo è raro.
  • Se si posiziona una striscia bianca sul nome del test, il test sarà difficile da leggere.Molti codici idraulici per la struttura fittizia nascondono il test effettivo eseguito.
  • richiede che tu impari questo particolare sapore di una struttura beffarda

Altri suggerimenti

Generalmente preferisco usare i mock a causa delle aspettative.Quando chiami un metodo su uno stub che restituisce un valore, in genere ti restituisce semplicemente un valore.Ma quando chiami un metodo su un mock, non solo restituisce un valore, ma rafforza anche l'aspettativa che hai impostato che il metodo sia stato chiamato in primo luogo.In altre parole, se imposti un'aspettativa e poi non chiami quel metodo, viene generata un'eccezione.Quando imposti un'aspettativa, stai essenzialmente dicendo "Se questo metodo non viene chiamato, qualcosa è andato storto". E il contrario è vero, se chiami un metodo su una mute e non hai impostato un'aspettativa, lancerà un'eccezione, essenzialmente dicendo "Ehi, cosa stai facendo chiamando questo metodo quando non te lo aspettavi."

A volte non vuoi aspettative su ogni metodo che stai chiamando, quindi alcuni framework di mocking consentiranno mock "parziali" che sono come ibridi mock/stub, in quanto vengono applicate solo le aspettative impostate e ogni altra chiamata al metodo viene trattata più simile a uno stub in quanto restituisce semplicemente un valore.

Un posto valido per utilizzare gli stub che mi viene in mente, tuttavia, è quando introduci i test nel codice legacy.A volte è semplicemente più semplice creare uno stub creando una sottoclasse della classe che stai testando piuttosto che refactoring tutto per rendere il mocking facile o addirittura possibile.

E a questo...

Evitare sempre l'uso di mock perché rendono fragili i test.I tuoi test ora hanno una conoscenza complessa dei metodi chiamati dall'implementazione, se l'interfaccia derisa cambia...i tuoi test si interrompono.Quindi usa il tuo miglior giudizio..<

...Dico che se la mia interfaccia cambia, è meglio che i miei test si interrompano.Perché il punto centrale dei test unitari è che testano accuratamente il mio codice così come esiste in questo momento.

È meglio usare una combinazione e dovrai usare il tuo giudizio.Ecco le linee guida che utilizzo:

  • Se effettuare una chiamata al codice esterno fa parte del comportamento previsto (rivolto verso l'esterno) del codice, è necessario testarlo.Usa una finta.
  • Se la chiamata è davvero un dettaglio implementativo di cui il mondo esterno non si preoccupa, preferisci gli stub.Tuttavia:
  • Se temi che implementazioni successive del codice testato possano accidentalmente aggirare i tuoi stub e vuoi notare se ciò accade, usa un mock.Stai accoppiando il tuo test al tuo codice, ma è per il gusto di notare che il tuo stub non è più sufficiente e il tuo test deve essere rielaborato.

Il secondo tipo di derisione è una sorta di male necessario.In realtà quello che sta succedendo qui è che, sia che usi uno stub o un mock, in alcuni casi devi accoppiarlo al tuo codice più di quanto vorresti.Quando ciò accade, è meglio usare un mock piuttosto che uno stub solo perché saprai quando l'accoppiamento si interrompe e il tuo codice non è più scritto nel modo in cui il tuo test pensava che sarebbe stato.Probabilmente è meglio lasciare un commento nel tuo test quando lo fai in modo che chiunque lo rompa sappia che il suo codice non è sbagliato, lo è il test.

E ancora una volta, questo è un odore di codice e l'ultima risorsa.Se ritieni di doverlo fare spesso, prova a ripensare al modo in cui scrivi i tuoi test.

Dipende solo dal tipo di test che stai facendo.Se stai eseguendo test basati sul comportamento, potresti volere una simulazione dinamica in modo da poter verificare che si verifichi qualche interazione con la tua dipendenza.Ma se stai eseguendo test basati sullo stato potresti volere uno stub in modo da verificare valori/ecc

Ad esempio, nel test seguente noti che elimino la vista in modo da poter verificare che sia impostato un valore di proprietà (test basato sullo stato).Quindi creo una simulazione dinamica della classe di servizio in modo da poter assicurarmi che un metodo specifico venga chiamato durante il test (test basato sull'interazione/sul comportamento).

<TestMethod()> _
Public Sub Should_Populate_Products_List_OnViewLoad_When_PostBack_Is_False()
    mMockery = New MockRepository()
    mView = DirectCast(mMockery.Stub(Of IProductView)(), IProductView)
    mProductService = DirectCast(mMockery.DynamicMock(Of IProductService)(), IProductService)
    mPresenter = New ProductPresenter(mView, mProductService)
    Dim ProductList As New List(Of Product)()
    ProductList.Add(New Product())
    Using mMockery.Record()
        SetupResult.For(mView.PageIsPostBack).Return(False)
        Expect.Call(mProductService.GetProducts()).Return(ProductList).Repeat.Once()
    End Using
    Using mMockery.Playback()
        mPresenter.OnViewLoad()
    End Using
    'Verify that we hit the service dependency during the method when postback is false
    Assert.AreEqual(1, mView.Products.Count)
    mMockery.VerifyAll()
End Sub

Non importa Statista vs.Interazione.Pensa ai ruoli e alle relazioni.Se un oggetto collabora con un vicino per portare a termine il suo lavoro, allora quella relazione (come espressa in un'interfaccia) è candidata per essere testata utilizzando mock.Se un oggetto è un oggetto di valore semplice con un po' di comportamento, testalo direttamente.Non vedo il motivo di scrivere mock (o anche stub) a mano.È così che abbiamo iniziato tutti e ci siamo allontanati da quello.

Per una discussione più lunga, considera di dare un'occhiata a http://www.mockobjects.com/book

Leggi la discussione di Luke Kanies esattamente su questa domanda in questo post del blog.Fa riferimento un post di Jay Fields il che suggerisce addirittura che utilizzare stub_everything [un equivalente di ruby/mocha] è preferibile per rendere i test più robusti.Per citare le ultime parole di Fields:"Mocha rende facile definire un mock così come definire uno stub, ma ciò non significa che dovresti sempre preferire i mock.In effetti, generalmente preferisco gli stub e utilizzo i mock quando necessario."

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top