Mock framework vs framework MS Fakes


99

Un po 'confuso sulle differenze tra framework Mock come NMock e VS 2011 Fakes Framework. Passando attraverso MSDN, quello che capisco è che i Fakes ti permettono di deridere le tue dipendenze proprio come RhinoMock o NMock, tuttavia l'approccio è diverso, Fakes genera codice per ottenere questa funzionalità ma il framework Mocks no. Quindi la mia comprensione è corretta? Fakes è solo un altro framework Mock

Risposte:


189

La tua domanda riguardava il modo in cui il framework MS Fakes è diverso da NMock e sembra che le altre risposte abbiano risolto alcuni di questi problemi, ma qui ci sono alcune informazioni in più su come sono gli stessi e come sono diversi. NMock è anche simile a RhinoMocks e Moq, quindi li sto raggruppando con NMock.

Ci sono 3 differenze principali che vedo subito tra NMock / RhinoMocks / Moq e MS Fakes Framework:

  • Il framework MS Fakes utilizza codice generato, molto simile a Accessors nelle versioni precedenti di Visual Studio invece di tipi generici. Quando si desidera utilizzare il framework falsi per una dipendenza, aggiungere l'assembly che contiene la dipendenza ai riferimenti del progetto di test e quindi fare clic con il pulsante destro del mouse su di esso per generare i doppi di test (stub o shim). Quindi, quando esegui il test, stai effettivamente utilizzando invece queste classi generate. NMock utilizza generici per realizzare la stessa cosa (cioè IStudentRepository studentRepository = mocks.NewMock<IStudentRepository>()). A mio parere, l'approccio del framework MS Fakes inibisce la navigazione del codice e il refactoring dall'interno dei test poiché stai effettivamente lavorando su una classe generata, non sulla tua vera interfaccia.

  • Il framework MS Fakes fornisce stub e talpe (shim), mentre NMock, RhinoMocks e Moq forniscono tutti stub e mock . Non capisco davvero la decisione di MS di non includere i mock e io personalmente non sono un fan dei nei per i motivi descritti di seguito.

  • Con il framework MS Fakes, fornisci un'implementazione alternativa dei metodi che desideri stub. All'interno di queste implementazioni alternative, è possibile specificare i valori restituiti e tenere traccia delle informazioni su come o se il metodo è stato chiamato. Con NMock, RhinoMocks e Moq, si genera un oggetto fittizio e quindi si utilizza quell'oggetto per specificare i valori di ritorno stub o per tenere traccia delle interazioni (se e come sono stati chiamati i metodi). Trovo l'approccio dei falsi alla SM più complesso e meno espressivo.

Per chiarire la differenza in ciò che forniscono i framework: NMock, RhinoMocks e Moq forniscono tutti due tipi di doppioni di test (stub e mock). La struttura dei falsi fornisce mozziconi e talpe (li chiamano spessori) e sfortunatamente non include simulazioni. Per comprendere le differenze e le somiglianze tra NMock e MS Fakes, è utile capire quali sono questi diversi tipi di doppi di test:

Stub: gli stub vengono utilizzati quando è necessario fornire valori per metodi o proprietà che verranno richiesti ai doppi di test dal metodo sottoposto a test. Ad esempio, quando il mio metodo sottoposto a test chiama il metodo DoesStudentExist () del doppio di test IStudentRepository, voglio che restituisca true.

L'idea degli stub nei falsi NMock e MS è la stessa, ma con NMock faresti qualcosa del genere:

Stub.On(mockStudentRepository).Method("DoesStudentExist").Will(Return.Value(true));

E con MSFakes faresti qualcosa di simile:

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() // Generated by Fakes.
{
    DoesStudentExistInt32 = (studentId) => { return new Student(); }
};

Si noti nell'esempio di MS Fakes che si crea un'implementazione completamente nuova per il metodo DoesStudentExist (si noti che si chiama DoesStudentExistInt32 perché il framework fakes aggiunge i tipi di dati dei parametri ai nomi dei metodi quando genera gli oggetti stub, penso che questo offuschi la chiarezza di i test). Ad essere onesti l'implementazione di NMock mi infastidisce anche perché usa una stringa per identificare il nome del metodo. (Perdonatemi se ho frainteso come si intende usare NMock.) Questo approccio inibisce davvero il refactoring e per questo motivo consiglio vivamente RhinoMocks o Moq su NMock.

Mock: i mock vengono utilizzati per verificare l'interazione tra il metodo sottoposto a test e le sue dipendenze. Con NMock, lo fai impostando aspettative simili a questa:

Expect.Once.On(mockStudentRepository).Method("Find").With(123);

Questo è un altro motivo per cui preferirei RhinoMocks e Moq su NMock, NMock utilizza il vecchio stile di aspettativa mentre RhinoMocks e Moq supportano entrambi l'approccio Arrange / Act / Assert in cui specifichi le interazioni previste come affermazioni alla fine del test come questo :

stubStudentRepository.AssertWasCalled( x => x.Find(123));

Ancora una volta, nota che RhinoMocks usa un lambda invece di una stringa per identificare il metodo. Il framework ms fakes non fornisce affatto mock. Ciò significa che nelle implementazioni bloccate (vedere la descrizione degli stub sopra) devi impostare le variabili che successivamente verifichi siano state impostate correttamente. Sarebbe simile a questo:

bool wasFindCalled = false;

IStudentRepository studentRepository = new DataAccess.Fakes.StubIStudentRepository() 
{
    DoesStudentExistInt32 = (studentId) => 
        { 
            wasFindCalled = true;
            return new Student(); 
        }
};

classUnderTest.MethodUnderTest();

Assert.IsTrue(wasFindCalled);

Trovo che questo approccio sia un po 'complicato poiché devi tenere traccia della chiamata nello stub e quindi affermarla più avanti nel test. Trovo che gli esempi NMock, e in particolare RhinoMocks, siano più espressivi.

Talpe (Shims): Ad essere sincero, non mi piacciono le talpe, a causa del loro potenziale uso improprio. Una delle cose che mi piacciono così tanto del test unitario (e del TDD in particolare) è che il test del codice ti aiuta a capire dove hai scritto codice scadente. Questo perché è difficile testare un codice scritto male. Questo non è vero quando si usano le talpe perché le talpe sono effettivamente progettate per consentire di testare dipendenze che non vengono iniettate o per testare metodi privati. Funzionano in modo simile agli stub, tranne per il fatto che usi un ShimsContext come questo:

using (ShimsContext.Create())
{
    System.Fakes.ShimDateTime.NowGet = () => { return new DateTime(fixedYear, 1, 1); };
}

La mia preoccupazione con gli spessori è che le persone inizieranno a vederli come "un modo più semplice per test unitari" perché non ti obbliga a scrivere codice nel modo in cui dovresti. Per un resoconto più completo su questo concetto, guarda questo mio post:

Per ulteriori informazioni su alcune preoccupazioni relative ai framework falsi, dai un'occhiata a questi post:

Se sei interessato a imparare RhinoMocks, ecco un video di formazione Pluralsight (divulgazione completa - ho scritto questo corso e vengo pagato le royalty per le visualizzazioni, ma penso che si applichi a questa discussione, quindi lo includo qui):


14
@ Jim Non sono d'accordo sul fatto che consentire a uno di "testare le dipendenze che non vengono iniettate" sia una cosa negativa. Al contrario, in pratica, questa capacità aiuta ad evitare di ingombrare una base di codice con molte interfacce inutili e la configurazione / complessità del "cablaggio degli oggetti" associata. Ho già visto alcuni progetti (sia Java che .NET) che avevano centinaia di interfacce Java / C # con una sola classe di implementazione e molte configurazioni XML inutili (per i bean Spring DI o in Web.config per MS Unity struttura). È meglio non iniettare tali dipendenze .
Rogério

19
@Rogerio, ho lavorato su più progetti con migliaia di classi che seguono questo modello e quando l'inserimento delle dipendenze è fatto bene (se stai usando la configurazione XML non lo stai facendo bene, imho), è semplice e relativamente indolore. D'altra parte, ho lavorato su sistemi che non lo fanno e l'accoppiamento tra le classi mi ha sempre causato un dolore significativo. Il test non è la ragione per l'utilizzo dell'inserimento delle dipendenze (sebbene non sia una cattiva ragione). Il vero motivo è l'accoppiamento allentato. Avere interfacce implementate da una singola classe non mi ha mai causato problemi.
Jim Cooper

17
@ Jim, vedo la capacità di testare un cattivo design come un grande vantaggio. La prima cosa che vuoi fare prima di refactoring del codice legacy verso un design migliore, è scrivere test.
Thomas Materna

4
La mia regola è che utilizzo Fakes solo quando devo shimare un metodo condiviso / statico o una classe a cui non ho accesso per implementare un'interfaccia. Per tutto il resto utilizzo Moq. I falsi sono più lenti (perché gli assembly falsi devono essere generati) e richiede uno sforzo di codifica maggiore per abbinare la funzionalità che si ottiene con Moq.
Nick

5
@ CarloV.Dango Prima di tutto so perfettamente che Dependency Injection non necessita di interfacce separate; il mio commento precedente stava solo alludendo alla tendenza a creare tali interfacce quando si utilizza DI. In secondo luogo, "IOC" (Inversion of Control) non ha nulla a che fare con DI! (Il primo riguarda l'inversione del flusso di controllo tra metodi, mentre il secondo riguarda la risoluzione delle dipendenze per un componente / oggetto.) Confondendo IOC e DI, sei tu a mostrare ignoranza, non io; e, francamente, questo sito non merita che le persone insultino gli altri, quindi per favore, manteniamolo civile.
Rogério

23

Hai ragione, ma c'è di più nella storia. Le cose più importanti da togliere da questa risposta sono:

  1. La tua architettura dovrebbe fare un uso corretto degli stub e dell'iniezione di dipendenza, piuttosto che fare affidamento sulla stampella di Fakes e mock

  2. I falsi e i mock sono utili per testare il codice che non dovresti o non puoi modificare, come ad esempio:

    • Codice legacy che non fa uso (o uso efficiente) di stub
    • API di terze parti
    • Risorse per le quali non hai codice sorgente

Shims (noto come "Moles", durante lo sviluppo) è effettivamente un quadro beffardo che funziona tramite chiamate deviate. Invece di costruire faticosamente una simulazione (sì, anche usare Moq è relativamente doloroso!), Gli shim usano semplicemente l'oggetto codice di produzione già in atto. Gli shim semplicemente reindirizzano la chiamata dalla destinazione di produzione al delegato di test.

Gli stub vengono generati dalle interfacce nel progetto di destinazione. L'oggetto Stub è proprio questo: un'implementazione dell'interfaccia. Il vantaggio dell'utilizzo del tipo Stub è che puoi generare rapidamente uno stub senza ingombrare il tuo progetto di test con molti stub di utilizzo una tantum, per non parlare del tempo perso per crearli. Ovviamente, dovresti comunque creare stub concreti, da utilizzare in molti test.

L'implementazione efficiente di Fakes (tipi di Shims, Mock e Stub) richiede un po 'di tempo per abituarsi, ma ne vale la pena. Ho risparmiato personalmente settimane di tempo di sviluppo, utilizzando i tipi Shims / Mole, Mocks e Stub. Spero che ti diverta con la tecnologia quanto me!


11
Downvoted basato sulla mancanza di specifiche e alcuni problemi di terminologia con i tuoi commenti su Fakes. Fakes è un termine generico per tutti i tipi di test double e anche il nome MS usato per la loro libreria di fake. Nella loro libreria solo gli spessori sono in realtà talpe, gli stub no. Per quanto riguarda la necessità di esempi, vorrei vedere un esempio nella tua risposta che mostra come creare uno shim (o stub) con Fakes sia più semplice rispetto all'utilizzo di Moq. Se apporti modifiche alla tua risposta per risolverli, cambierò il mio voto negativo.
Jim Cooper

1
Ma aggiunge una buona giustificazione per l'uso di shim (talpe), cioè codice di terze parti, API di sistema / SDK. Non si tratta solo di lavorare con le proprie soluzioni interne. Quindi ti do un voto per pareggiare quello :-)
Tony Wall

2
@ Mike e Jim .. Mi sono sforzato di correggere la terminologia utilizzata in questa risposta. Per favore fatemi sapere se possiamo migliorarlo. Grazie.
Snesh

15

A quanto ho capito, il team di Visual Studio voleva evitare di competere con le varie librerie fittizie disponibili per .NET. La SM deve spesso affrontare decisioni difficili come questa. Vengono bloccati se non forniscono determinate funzionalità ("perché MS non ci fornisce una libreria fittizia; i mock sono un requisito così comune?") E dannati se lo fanno ("perché Microsoft agisce in modo così aggressivo e sostenitori naturali fuori mercato? ") Molto spesso, ma non sempre, decidono di trattenersi dal fornire semplicemente la propria alternativa alle tecnologie disponibili e ben accolte. Questo sembra essere il caso qui.

La funzione di spessore di Fakes è davvero molto utile. Certo, ci sono pericoli. Ci vuole un po 'di disciplina per assicurarti di usarlo solo dove necessario. Tuttavia, colma una grande lacuna. La mia lamentela principale è che viene fornito solo con l'edizione Ultimate di VS 2012 e quindi sarà disponibile solo per una sottosezione della comunità di sviluppo .NET. Che peccato.


2
Il framework dei falsi è disponibile anche con l'edizione Premium.
AlwaysAProgrammer

13

Fakes include due diversi tipi di oggetti "falsi". Il primo, chiamato "stub", è essenzialmente un dummy generato automaticamente il cui comportamento predefinito può (e di solito sarebbe) essere sovrascritto per renderlo un mock più interessante. Tuttavia, mancano alcune delle funzionalità offerte dalla maggior parte dei framework di mocking attualmente disponibili. Ad esempio, se si desidera verificare che sia stato richiamato un metodo su un'istanza stub, è necessario aggiungere la logica per questo da soli. Fondamentalmente, se stai creando manualmente i tuoi mock ora, gli stub probabilmente sembrerebbero un miglioramento. Tuttavia, se stai già utilizzando un framework di derisione più completo, potresti sentire che mancano alcuni pezzi importanti dagli stub Fakes.

L'altra categoria di oggetti offerta da Fakes, chiamata "shim", espone un meccanismo per sostituire il comportamento delle dipendenze che non sono state (o non possono essere) disaccoppiate adeguatamente per la sostituzione standard tramite mock. AFAIK, TypeMock è l'unico dei principali framework di mocking che attualmente offre questo tipo di funzionalità.

A proposito, se hai già provato Moles, Fakes è essenzialmente la stessa cosa, finalmente uscendo da Microsoft Research e trasformandosi in un prodotto reale.


1
Aggiornamento: Moles è stato ora integrato in MS Fakes. "Il framework Fakes in Visual Studio 2012 è la prossima generazione di Moles & Stubs. Fakes è diverso da Moles, tuttavia, il passaggio da Moles a Fakes richiederà alcune modifiche al codice. Il framework Moles non sarà supportato in Visual Studio 2012 . " Fonte: research.microsoft.com/en-us/projects/moles
Sire

1

Per quanto riguarda gli oggetti Fake (Shim + Stub), è stato ben definito sopra, anche se credo che l'ultimo paragrafo dell'ultimo commento riassuma abbastanza bene l'intera situazione.

Sebbene molte persone sosterranno che gli oggetti falsi (Shim + Stub) sono buone risorse da avere in alcuni casi di unit test, lo svantaggio è che non importa se stai usando Visual Studio 2012 o Visual Studio 2013, queste opzioni sono SOLO disponibili con le versioni Premium o Ultimate. IOW, questo significa che NON dovrai eseguire NESSUNO di quei falsi (Shim + Stub) su nessuna versione Pro.

Probabilmente potresti vedere l'opzione di menu Fakes (Shim + Stub) nelle versioni Pro, ma attenzione, ci sono alcune probabilità piuttosto elevate che non finirai con assolutamente nulla ... Non genererà alcun errore di compilazione che ti dice che qualcosa di importante manca, le opzioni non ci sono, quindi non perdere tempo ...

È un fattore importante da considerare in un team di sviluppo, specialmente se uno è l'unico che utilizza la versione Ultimate mentre tutti gli altri usano la versione Pro ... Moq d'altra parte può essere facilmente installato tramite Nuget indipendentemente dalla versione di Visual Studio che usi. Non ho avuto problemi a usare Moq, la chiave con qualsiasi strumento è sapere a cosa servono e come usarli correttamente;)


1
Formatta la tua domanda in paragrafi più piccoli in modo che sia più facile da leggere.

@SirXamelot, non refactoring del codice non è possibile modificare l'output delle chiamate statiche fornite da .net, ad esempio DateTime.Now o Guid.NewGuid
zaitsman
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.