Ho letto vari articoli su derisione e stub nei test, tra cui Mock Aren't Stubs di Martin Fowler , ma ancora non capisco la differenza.
Ho letto vari articoli su derisione e stub nei test, tra cui Mock Aren't Stubs di Martin Fowler , ma ancora non capisco la differenza.
Risposte:
mozzicone
Credo che la più grande distinzione sia che uno stub che hai già scritto con un comportamento predeterminato. Quindi avresti una classe che implementa la dipendenza (classe astratta o interfaccia molto probabilmente) che stai fingendo a scopo di test e i metodi verrebbero semplicemente eliminati con risposte impostate. Non farebbero niente di speciale e avresti già scritto il codice stubbing per farlo al di fuori del tuo test.
finto
Un finto è qualcosa che come parte del tuo test devi impostare con le tue aspettative. Un finto non è impostato in un modo predeterminato, quindi hai un codice che lo fa nel tuo test. Le simulazioni in un certo senso sono determinate in fase di esecuzione poiché il codice che imposta le aspettative deve essere eseguito prima di fare qualsiasi cosa.
Differenza tra mock e mozziconi
I test scritti con beffe di solito seguono un initialize -> set expectations -> exercise -> verify
modello di test. Mentre lo stub pre-scritto avrebbe seguito un initialize -> exercise -> verify
.
Somiglianza tra Mock e Stub
Lo scopo di entrambi è quello di eliminare il test di tutte le dipendenze di una classe o di una funzione in modo che i test siano più focalizzati e più semplici in ciò che stanno cercando di dimostrare.
Esistono diverse definizioni di oggetti che non sono reali. Il termine generale è doppio del test . Questo termine comprende: manichino , falso , troncone , finto .
Secondo l'articolo di Martin Fowler :
- Gli oggetti fittizi vengono fatti passare ma mai effettivamente utilizzati. Di solito vengono utilizzati solo per riempire elenchi di parametri.
- Gli oggetti falsi in realtà hanno implementazioni funzionanti, ma di solito prendono alcuni collegamenti che li rendono non adatti alla produzione (un database in memoria è un buon esempio).
- Gli stub forniscono risposte predefinite alle chiamate effettuate durante il test, di solito non rispondono a nulla al di fuori di ciò che è programmato per il test. Gli stub possono anche registrare informazioni sulle chiamate, come uno stub del gateway di posta elettronica che ricorda i messaggi che ha "inviato" o forse solo quanti messaggi ha "inviato".
- Le finte sono ciò di cui stiamo parlando qui: oggetti pre-programmati con aspettative che formano una specifica delle chiamate che dovrebbero ricevere.
Mock vs Stubs = Test comportamentali vs test di stato
Secondo il principio di Test solo una cosa per test , in un test possono esserci più tronchetti, ma generalmente c'è solo un finto.
Prova il ciclo di vita con gli stub:
Prova il ciclo di vita con simulazioni:
Sia i test di simulazione che quelli di stub danno una risposta alla domanda: qual è il risultato?
Anche i test con simulazioni sono interessati a: come è stato raggiunto il risultato?
Uno stub è un semplice oggetto falso. Si assicura solo che il test venga eseguito senza problemi.
Un finto è un troncone più intelligente. Verifica che il tuo test lo superi.
Ecco una descrizione di ciascuno seguita da un campione del mondo reale.
Manichino - solo valori fasulli per soddisfare il API
.
Esempio : se si sta testando un metodo di una classe che richiede molti parametri obbligatori in un costruttore che non hanno alcun effetto sul test, è possibile creare oggetti fittizi allo scopo di creare nuove istanze di una classe.
Falso : crea un'implementazione di prova di una classe che potrebbe dipendere da un'infrastruttura esterna. (È buona norma che il test unitario NON interagisca effettivamente con l'infrastruttura esterna.)
Esempio : creare un'implementazione falsa per accedere a un database, sostituirlo con la
in-memory
raccolta.
Stub : sovrascrive i metodi per restituire valori codificati, indicati anche come state-based
.
Esempio : la classe di test dipende dal completamento di un metodo che
Calculate()
richiede 5 minuti. Invece di attendere 5 minuti, è possibile sostituire la sua vera implementazione con stub che restituisce valori codificati; prendendo solo una piccola parte del tempo.
Finto - molto simile Stub
ma interaction-based
piuttosto che basato sullo stato. Ciò significa che non ti aspetti Mock
di restituire un valore, ma di presumere che vengano effettuate specifiche chiamate di metodo.
Esempio: stai testando una classe di registrazione utente. Dopo aver chiamato
Save
, dovrebbe chiamareSendConfirmationEmail
.
Stubs
e in Mocks
realtà sono sottotipi di Mock
, entrambi scambiano l'implementazione reale con l'implementazione del test, ma per motivi diversi e specifici.
Nel corso codeschool.com , Rails Testing for Zombies , danno questa definizione dei termini:
mozzicone
Per sostituire un metodo con codice che restituisce un risultato specificato.
finto
Uno stub con un'affermazione che il metodo viene chiamato.
Quindi, come ha descritto Sean Copenhaver nella sua risposta, la differenza è che le beffe fissano aspettative (cioè fanno affermazioni, se o come vengono chiamate).
Gli stub non falliscono i tuoi test, finto possibile.
Penso che la risposta più semplice e chiara a questa domanda sia data da Roy Osherove nel suo libro The art of Unit Testing (pagina 85)
Il modo più semplice per dire che abbiamo a che fare con uno stub è notare che lo stub non può mai fallire il test. Le asserzioni che il test usa sono sempre contro la classe sotto test.
D'altra parte, il test utilizzerà un oggetto simulato per verificare se il test ha avuto esito negativo o meno. [...]
Ancora una volta, l'oggetto finto è l'oggetto che usiamo per vedere se il test fallisce o no.
Ciò significa che se stai affermando contro il falso significa che stai usando il falso come un finto, se stai usando il falso solo per eseguire il test senza affermazione su di esso stai usando il falso come un troncone.
Leggendo tutte le spiegazioni sopra, fammi provare a condensare:
Un Mock sta solo testando il comportamento, assicurandosi che vengano chiamati determinati metodi. Uno stub è una versione verificabile (di per sé) di un particolare oggetto.
Cosa intendi con Apple?
Se lo confronti con il debug:
Stub è come assicurarsi che un metodo restituisca il valore corretto
Mock è come entrare nel metodo e assicurarsi che tutto all'interno sia corretto prima di restituire il valore corretto.
Utilizzando un modello mentale mi ha davvero aiutato a capire questo, piuttosto che tutte le spiegazioni e gli articoli, che non si è "affondato".
Immagina che tuo figlio abbia una lastra di vetro sul tavolo e inizi a giocarci. Ora, hai paura che si rompa. Quindi, invece, gli dai un piatto di plastica. Sarebbe una simulazione (stesso comportamento, stessa interfaccia, implementazione "più morbida").
Ora, supponi di non avere la sostituzione in plastica, quindi spieghi "Se continui a giocarci, si romperà!". Questo è uno stub , hai fornito in anticipo uno stato predefinito.
Un manichino sarebbe la forchetta che non ha nemmeno usato ... e una spia potrebbe essere qualcosa come fornire la stessa spiegazione che hai già usato che ha funzionato.
Penso che la differenza più importante tra loro sia le loro intenzioni.
Vorrei provare a spiegarlo in WHY stub vs. WHY mock
Supponiamo che stia scrivendo un codice di prova per il controller della cronologia pubblica del mio client mac twitter
Ecco il codice di esempio di prova
twitter_api.stub(:public_timeline).and_return(public_timeline_array)
client_ui.should_receive(:insert_timeline_above).with(public_timeline_array)
controller.refresh_public_timeline
Scrivendo finto, scopri la relazione di collaborazione degli oggetti verificando che le aspettative siano soddisfatte, mentre il troncone simula solo il comportamento dell'oggetto.
Suggerisco di leggere questo articolo se stai cercando di saperne di più sui mock: http://jmock.org/oopsla2004.pdf
Per essere molto chiari e pratici:
Stub: una classe o un oggetto che implementa i metodi della classe / oggetto da falsificare e restituisce sempre ciò che si desidera.
Esempio in JavaScript:
var Stub = {
method_a: function(param_a, param_b){
return 'This is an static result';
}
}
Mock: lo stesso di stub, ma aggiunge una logica che "verifica" quando viene chiamato un metodo in modo da poter essere certi che qualche implementazione stia chiamando quel metodo.
Come dice @mLevan, immagina come esempio che stai testando una classe di registrazione utente. Dopo aver chiamato Salva, dovrebbe chiamare SendConfirmationEmail.
Un codice molto stupido Esempio:
var Mock = {
calls: {
method_a: 0
}
method_a: function(param_a, param_b){
this.method_a++;
console.log('Mock.method_a its been called!');
}
}
Questa diapositiva spiega molto bene le principali differenze.
* Da CSE 403 Lecture 16, University of Washington (slide creata da "Marty Stepp")
Mi piace la spiegazione fornita da Roy Osherove [collegamento video] .
Ogni classe o oggetto creato è un falso. È un Mock se si verificano chiamate contro di esso. Altrimenti è un troncone.
vediamo Doppi test:
Stub : Stub è un oggetto che contiene dati predefiniti e lo utilizza per rispondere alle chiamate durante i test. Ad esempio : un oggetto che deve acquisire alcuni dati dal database per rispondere a una chiamata del metodo.
Mock : i mock sono oggetti che registrano le chiamate che ricevono. Nell'affermazione di prova, possiamo verificare su Mock che tutte le azioni previste sono state eseguite. Ad esempio : una funzionalità che chiama il servizio di invio di posta elettronica. per di più basta controllare questo .
Un falso è un termine generico che può essere utilizzato per descrivere uno stub o un oggetto finto (scritto a mano o in altro modo), perché entrambi sembrano l'oggetto reale.
Se un falso è uno stub o un finto dipende da come viene utilizzato nel test corrente. Se viene usato per verificare un'interazione (asserito contro), è un oggetto finto. Altrimenti, è un troncone.
Fakes assicura che il test venga eseguito senza problemi. Significa che il lettore del tuo test futuro capirà quale sarà il comportamento dell'oggetto falso, senza bisogno di leggere il suo codice sorgente (senza dipendere da risorse esterne).
Cosa significa eseguire correttamente i test?
Esempio di esempio nel codice seguente:
public void Analyze(string filename)
{
if(filename.Length<8)
{
try
{
errorService.LogError("long file entered named:" + filename);
}
catch (Exception e)
{
mailService.SendEMail("admin@hotmail.com", "ErrorOnWebService", "someerror");
}
}
}
Vuoi testare il metodo mailService.SendEMail () , per fare ciò devi simulare un'eccezione nel tuo metodo di test, quindi devi solo creare una classe errorSub Fake Stub per simulare quel risultato, quindi il tuo codice di test sarà in grado di testare metodo mailService.SendEMail (). Come vedi, devi simulare un risultato che proviene da un'altra classe ErrorService di dipendenza esterna.
Proprio dalla carta Ruoli Mock, non oggetti , dagli sviluppatori di jMock:
Gli stub sono implementazioni fittizie del codice di produzione che restituiscono risultati predefiniti. Gli oggetti simulati agiscono come tronconi, ma includono anche asserzioni per strumentare le interazioni dell'oggetto bersaglio con i suoi vicini.
Quindi, le differenze principali sono:
Per riassumere, cercando anche di dissipare la confusione dal titolo dell'articolo di Fowler : le beffe sono tronconi, ma non sono solo tronconi .
Stavo leggendo The Art of Unit Testing e mi sono imbattuto nella seguente definizione:
Un falso è un termine generico che può essere utilizzato per descrivere uno stub o un oggetto finto (scritto a mano o in altro modo), perché entrambi sembrano l'oggetto reale. Se un falso è uno stub o un finto dipende da come viene utilizzato nel test corrente. se è usato per controllare un'interazione (asserito contro), è un oggetto finto . Altrimenti, è un troncone .
Mi sono imbattuto in questo interessante articolo di UncleBob The Little Mocker . Spiega tutta la terminologia in un modo molto facile da capire, quindi è utile per i principianti. L'articolo di Martin Fowlers è una lettura difficile soprattutto per i principianti come me.
mozzicone ci aiuta a eseguire il test. Come? Fornisce valori che aiutano a eseguire il test. Questi valori non sono reali e abbiamo creato questi valori solo per eseguire il test. Ad esempio creiamo una HashMap per darci valori simili a quelli nella tabella del database. Quindi, invece di interagire direttamente con il database, interagiamo con Hashmap.
Il finto è un oggetto falso che esegue il test. dove abbiamo affermato.
Vedi sotto esempio di mock vs stub usando framework C # e Moq. Moq non ha una parola chiave speciale per Stub ma puoi usare anche l'oggetto Mock per creare stub.
namespace UnitTestProject2
{
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
[TestClass]
public class UnitTest1
{
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method calls Repository GetName method "once" when Id is greater than Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Once);
}
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method doesn't call Repository GetName method when Id is Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(0);
// Assert
mockEntityRepository.Verify(m => m.GetName(It.IsAny<int>()), Times.Never);
}
/// <summary>
/// Test using Stub to Verify that GetNameWithPrefix method returns Name with a Prefix
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
{
// Arrange
var stubEntityRepository = new Mock<IEntityRepository>();
stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
.Returns("Stub");
const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
var entity = new EntityClass(stubEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
}
}
public class EntityClass
{
private IEntityRepository _entityRepository;
public EntityClass(IEntityRepository entityRepository)
{
this._entityRepository = entityRepository;
}
public string Name { get; set; }
public string GetNameWithPrefix(int id)
{
string name = string.Empty;
if (id > 0)
{
name = this._entityRepository.GetName(id);
}
return "Mr. " + name;
}
}
public interface IEntityRepository
{
string GetName(int id);
}
public class EntityRepository:IEntityRepository
{
public string GetName(int id)
{
// Code to connect to DB and get name based on Id
return "NameFromDb";
}
}
}
Punto di vista del test Stub e Mock:
Stub è un'implementazione fittizia eseguita dall'utente in modo statico , ovvero quando Stub scrive il codice di implementazione. Quindi non è in grado di gestire la definizione del servizio e le condizioni dinamiche, normalmente ciò avviene nel framework JUnit senza usare il framework beffardo.
Mock è anche un'implementazione fittizia ma la sua implementazione ha fatto un modo dinamico usando framework Mocking come Mockito. Quindi possiamo gestire la definizione delle condizioni e del servizio come un modo dinamico, cioè i mock possono essere creati dinamicamente dal codice in fase di esecuzione. Quindi usando la simulazione possiamo implementare gli Stub in modo dinamico.
Più risposte utili, uno dei punti più potenti dell'uso di Mocks of Subs
Se il collaboratore [da cui dipende il codice principale] non è sotto il nostro controllo (ad es. Da una libreria di terze parti),
In questo caso, stub è più difficile da scrivere piuttosto che deridere .
Ho usato esempi di Python nella mia risposta per illustrare le differenze.
Stub - Stubbing è una tecnica di sviluppo software utilizzata per implementare metodi di classi all'inizio del ciclo di vita dello sviluppo. Vengono utilizzati comunemente come segnaposto per l'implementazione di un'interfaccia nota, in cui l'interfaccia è finalizzata o nota ma l'implementazione non è ancora nota o finalizzata. Inizi con gli stub, il che significa semplicemente che scrivi la definizione di una funzione e lasci il codice effettivo per dopo. Il vantaggio è che non dimenticherai i metodi e puoi continuare a pensare al tuo progetto mentre lo vedi nel codice. Puoi anche fare in modo che il tuo stub restituisca una risposta statica in modo che la risposta possa essere utilizzata immediatamente da altre parti del codice. Gli oggetti Stub forniscono una risposta valida, ma è statico indipendentemente dall'input che passi, otterrai sempre la stessa risposta:
class Foo(object):
def bar1(self):
pass
def bar2(self):
#or ...
raise NotImplementedError
def bar3(self):
#or return dummy data
return "Dummy Data"
finto oggetti vengono utilizzati in casi di test simulati e convalidano il fatto che determinati metodi vengano chiamati su tali oggetti. Gli oggetti simulati sono oggetti simulati che imitano il comportamento di oggetti reali in modo controllato. Generalmente si crea un oggetto simulato per testare il comportamento di qualche altro oggetto. Le simulazioni ci permettono di simulare risorse che non sono disponibili o troppo ingombranti per i test unitari.
mymodule.py:
import os
import os.path
def rm(filename):
if os.path.isfile(filename):
os.remove(filename)
test.py:
from mymodule import rm
import mock
import unittest
class RmTestCase(unittest.TestCase):
@mock.patch('mymodule.os')
def test_rm(self, mock_os):
rm("any path")
# test that rm called os.remove with the right parameters
mock_os.remove.assert_called_with("any path")
if __name__ == '__main__':
unittest.main()
Questo è un esempio molto semplice che esegue semplicemente rm e afferma il parametro con cui è stato chiamato. Puoi usare mock con oggetti non solo funzioni come mostrato qui, e puoi anche restituire un valore così un oggetto mock può essere usato per sostituire uno stub per il test.
Altro su unittest.mock , la nota in python 2.x mock non è inclusa in unittest ma è un modulo scaricabile che può essere scaricato tramite pip (pip install mock).
Ho anche letto "The Art of Unit Testing" di Roy Osherove e penso che sarebbe fantastico se un libro simile venisse scritto usando esempi di Python e Python. Se qualcuno conosce un libro del genere, ti preghiamo di condividere. Saluti :)
Uno stub è un oggetto falso creato a scopo di test. Un mock è uno stub che registra se si sono effettivamente verificate chiamate attese.
Uno stub è una funzione vuota che viene utilizzata per evitare eccezioni non gestite durante i test:
function foo(){}
Una simulazione è una funzione artificiale che viene utilizzata per evitare dipendenze del sistema operativo, dell'ambiente o dell'hardware durante i test:
function foo(bar){ window = this; return window.toString(bar); }
In termini di affermazioni e stato:
Riferimenti
molte risposte valide lassù ma penso che valga la pena menzionare questo modulo zio bob: https://8thlight.com/blog/uncle-bob/2014/05/14/TheLittleMocker.html
la migliore spiegazione di sempre con esempi!
Un finto è sia un oggetto tecnico che funzionale .
La simulazione è tecnica . È infatti creato da una libreria beffarda (EasyMock, JMockit e più recentemente Mockito sono noti per questi) grazie alla generazione del codice byte .
L'implementazione fittizia viene generata in un modo in cui potremmo strumentarla per restituire un valore specifico quando viene invocato un metodo, ma anche altre cose come la verifica che un metodo simulato sia stato invocato con alcuni parametri specifici (controllo rigoroso) o qualunque sia il parametro ( nessun controllo rigoroso).
Istantanea di un finto:
@Mock Foo fooMock
Registrare un comportamento:
when(fooMock.hello()).thenReturn("hello you!");
Verifica di una chiamata:
verify(fooMock).hello()
Questi chiaramente non sono il modo naturale di istanziare / scavalcare la classe / il comportamento di Foo. Ecco perché mi riferisco ad un aspetto tecnico.
Ma la simulazione è anche funzionale perché è un'istanza della classe che dobbiamo isolare dal SUT. E con comportamenti registrati su di esso, potremmo usarlo nel SUT allo stesso modo di come faremmo con uno stub.
Lo stub è solo un oggetto funzionale : questa è un'istanza della classe che dobbiamo isolare dal SUT e basta. Ciò significa che sia la classe stub che tutti i dispositivi di comportamento necessari durante i nostri test unitari devono essere definiti in modo esplicito.
Ad esempio per stub hello()
sarebbe necessario sottoclassare la Foo
classe (o implementare la sua interfaccia ce l'ha) e sovrascrivere hello()
:
public class HelloStub extends Hello{
public String hello {
return "hello you!";
}
}
Se un altro scenario di test richiede un altro valore restituito, probabilmente avremmo bisogno di definire un modo generico per impostare il rendimento:
public class HelloStub extends Hello{
public HelloStub(String helloReturn){
this.helloReturn = helloReturn;
}
public String hello {
return helloReturn;
}
}
Altro scenario: se avessi avuto un metodo di effetto collaterale (nessun ritorno) e avrei verificato che quel metodo fosse invocato, avrei probabilmente aggiunto un valore booleano o un contatore nella classe stub per contare quante volte è stato invocato il metodo.
Conclusione
Lo stub richiede spesso molto overhead / codice per scrivere per il test unitario. Ciò che il finto impedisce grazie alla fornitura immediata di funzionalità di registrazione / verifica.
Ecco perché al giorno d'oggi l'approccio stub viene raramente utilizzato in pratica con l'avvento di eccellenti librerie finte.
Informazioni sull'articolo di Martin Fowler: Non penso di essere un programmatore "beffardo" mentre uso le beffe ed evito gli stub.
Ma uso il finto quando è veramente necessario (dipendenze fastidiose) e preferisco i test di suddivisione in test e mini-integrazione quando collaudo una classe con dipendenze che il deridere sarebbe un sovraccarico.