Che cosa è beffardo?


Risposte:


598

Prologo: Se cerchi il nome finto nel dizionario, scoprirai che una delle definizioni della parola è qualcosa di fatto come un'imitazione .


Il derisione viene utilizzato principalmente nei test unitari. Un oggetto in prova può avere dipendenze da altri oggetti (complessi). Per isolare il comportamento dell'oggetto si desidera sostituire gli altri oggetti con simulazioni che simulano il comportamento degli oggetti reali. Ciò è utile se gli oggetti reali non sono pratici da incorporare nel test unitario.

In breve, deridere sta creando oggetti che simulano il comportamento di oggetti reali.


A volte potresti voler distinguere tra beffa e non stub . Potrebbe esserci un disaccordo su questo argomento, ma la mia definizione di stub è un oggetto simulato "minimo". Lo stub implementa il comportamento sufficiente per consentire all'oggetto in prova di eseguire il test.

Un mock è come un troncone ma il test verificherà anche che l'oggetto sotto test chiama il mock come previsto. Parte del test sta verificando che la simulazione sia stata utilizzata correttamente.

Per fare un esempio: è possibile stub un database implementando una semplice struttura in memoria per l'archiviazione dei record. L'oggetto in prova può quindi leggere e scrivere record nello stub del database per consentirgli di eseguire il test. Ciò potrebbe testare alcuni comportamenti dell'oggetto non correlati al database e lo stub del database verrebbe incluso solo per consentire l'esecuzione del test.

Se invece si desidera verificare che l'oggetto in prova scriva alcuni dati specifici nel database, sarà necessario deridere il database. Il test includerebbe quindi le affermazioni su ciò che è stato scritto nella simulazione del database.


18
Questa è una buona risposta, ma limita inutilmente il concetto di deridere gli oggetti . Sostituire "oggetto" con "unità" lo renderebbe più generale.
Rogério,

1
Capisco la differenza tra stub e finto. L'unica cosa è che se stai testando i tuoi casi con uno stub e questi non riescono a concludere che stai già utilizzando lo stub, quindi non hai più bisogno della verifica ?
Miele

91

Altre risposte spiegano cosa è beffardo. Lascia che ti accompagni attraverso diversi esempi . E credimi, in realtà è molto più semplice di quanto pensi.

tl; dr È un'istanza della classe originale. Ha altri dati iniettati in modo da evitare di testare le parti iniettate e concentrarsi esclusivamente sul test dei dettagli di implementazione della classe / funzioni.

Un semplice esempio:

class Foo {
    func add (num1: Int, num2: Int) -> Int { // Line A 
        return num1 + num2 // Line B
    }
}

let unit = Foo() // unit under test
assertEqual(unit.add(1,5),6)

Come puoi vedere, non sto testando LineA, cioè non sto convalidando i parametri di input. Non sto convalidando per vedere se num1, num2 sono numeri interi. Non ho affermazioni contrarie.

Sto solo testando per vedere se LineB (la mia implementazione ) ha dato i valori derisi 1e 5sta facendo come mi aspetto.

Ovviamente nella parola reale questo può diventare molto più complesso. I parametri possono essere un oggetto personalizzato come Persona, Indirizzo, oppure i dettagli dell'implementazione possono essere più di un singolo +. Ma la logica dei test sarebbe la stessa.

Esempio non codificante:

Supponiamo che tu stia costruendo una macchina che identifica il tipo e la marca dei dispositivi elettronici per la sicurezza di un aeroporto. La macchina lo fa elaborando ciò che vede con la sua fotocamera.

Ora il tuo manager entra dalla porta e ti chiede di testarlo.

Quindi come sviluppatore puoi portare 1000 oggetti reali, come un MacBook Pro, Google Nexus, una banana, un iPad ecc. Di fronte a esso e testare e vedere se tutto funziona.

Ma puoi anche usare oggetti derisi , come un MacBook pro identico (senza parti interne reali) o una banana di plastica di fronte. Puoi salvarti dall'investire in 1000 laptop reali e banane in decomposizione.

Il punto è che non stai cercando di provare se la banana è falsa o no. Né test se il laptop è falso o meno. Tutto quello che stai facendo sta testando se la vostra macchina una volta che vede una banana direbbe not an electronic devicee per un MacBook Pro direbbe: Laptop, Apple. Per la macchina, il risultato del suo rilevamento dovrebbe essere lo stesso per l'elettronica falsa / beffarda e l'elettronica reale

La logica sopra menzionata si applica anche ai test unitari del codice reale. Questa è una funzione che dovrebbe funzionare allo stesso modo con valori reali ottenuti da input (e interazioni) reali o derisivalori iniettati durante il test unitario. E proprio come ti salvi dall'uso di una vera banana o MacBook, con unit test (e derisione) ti risparmi di dover fare qualcosa che induca il tuo server a restituire un codice di stato di 500, 403, 200, ecc. (Forzando il tuo server per attivare 500 è solo quando il server è spento, mentre 200 è quando il server è attivo. È difficile eseguire 100 test incentrati sulla rete se devi attendere costantemente 10 secondi tra il passaggio del server su e giù). Quindi invece si inietta / deride una risposta con il codice di stato 500, 200, 403, ecc. E si verifica l'unità / funzione con un valore iniettato / deriso.

Esempio di codifica:

Supponiamo che tu stia scrivendo un'applicazione iOS e che tu abbia chiamate di rete. Il tuo compito è testare la tua applicazione. Testare / identificare se le chiamate di rete funzionano o meno come previsto NON È LA TUA RESPONSABILITÀ. È responsabilità di un'altra parte (team server) testarlo. È necessario rimuovere questa dipendenza (di rete) e tuttavia continuare a testare tutto il codice che lo circonda .

Una chiamata di rete può restituire diversi codici di stato 404, 500, 200, 303, ecc. Con una risposta JSON.

La tua app dovrebbe funzionare per tutti (in caso di errori, l'app dovrebbe generare l'errore previsto). Quello che fai con il beffardo è creare risposte di rete "immaginarie - simili alle reali" (come un codice 200 con un file JSON) e testare il tuo codice senza "effettuare la chiamata di rete reale e attendere la risposta della tua rete". È possibile codificare / restituire manualmente la risposta di rete per TUTTI i tipi di risposte di rete e vedere se l'app funziona come previsto. ( non assumerai mai / testerai un 200 con dati errati, perché non è tua responsabilità, la tua responsabilità è testare la tua app con un 200 corretto o, nel caso di un 400, 500, testare se l'app genera l'errore giusto)

Questo immaginario creativo, simile al reale, è noto come beffardo.

Per fare ciò, non puoi usare il tuo codice originale (il tuo codice originale non ha le risposte pre-inserite, giusto?). È necessario aggiungere qualcosa ad esso, iniettare / inserto che i dati fittizi che non è normalmente necessaria (o una parte della vostra classe).

Quindi crei un'istanza della classe originale e aggiungi qualunque cosa (qui essendo la risposta HTTP di rete, i dati O in caso di errore, passi la stringa di errore corretta, risposta HTTP) di cui hai bisogno e quindi testa la classe derisa .

Per farla breve, beffardo è semplificare e limitare ciò che stai testando e anche farti nutrire da cosa dipende una classe. In questo esempio si evita di testare la rete si chiama , e invece di prova se la vostra applicazione funziona come ci si aspetta con le uscite / risposte iniettate - dal beffardo classi

Inutile dire che si testa ogni risposta di rete separatamente.


Ora una domanda che ho sempre avuto in mente era: i contratti / punti finali e sostanzialmente la risposta JSON delle mie API vengono costantemente aggiornati. Come posso scrivere unit test che tengano conto di ciò?

Per approfondire questo: diciamo che il modello richiede una chiave / campo chiamato username. Si verifica questo e il test passa. 2 settimane dopo il backend modifica il nome della chiave in id. I tuoi test ancora superano. destra? o no?

È responsabilità dello sviluppatore back-end aggiornare le beffe. Dovrebbe essere parte del nostro accordo che forniscono derisioni aggiornate?

La risposta al problema di cui sopra è che: unit test + il processo di sviluppo come sviluppatore lato client dovrebbe / verrebbe rilevato una risposta derisa obsoleta. Se mi chiedi come? bene la risposta è:

La nostra app attuale fallirebbe (o non fallirebbe ancora non avere il comportamento desiderato) senza usare le API aggiornate ... quindi se ciò fallisce ... faremo modifiche sul nostro codice di sviluppo. Il che porta di nuovo al fallimento dei nostri test .... che dovremo correggere. (In realtà se dobbiamo eseguire correttamente il processo TDD non dobbiamo scrivere alcun codice sul campo a meno che non scriviamo il test per esso ... e vederlo fallire e poi andare a scrivere il codice di sviluppo effettivo per esso.)

Tutto ciò significa che il backend non deve dire: "hey abbiamo aggiornato i mock" ... alla fine succede attraverso lo sviluppo / debug del tuo codice. ‌ّ Perché fa tutto parte del processo di sviluppo! Anche se il backend fornisce la risposta derisa per te, allora è più facile.

Il mio punto su questo è che (se non puoi automatizzare ottenere una risposta API derisa aggiornata allora) sono richieste alcune interazioni umane, ad esempio aggiornamenti manuali di JSON e riunioni brevi per assicurarsi che i loro valori siano aggiornati diventeranno parte del tuo processo

Questa sezione è stata scritta grazie a una discussione lenta nel nostro gruppo Meetup CocoaHead


Solo per sviluppatori iOS:

Un ottimo esempio di derisione è questo discorso orientato al protocollo pratico di Natasha Muraschev . Passa al minuto 18:30, anche se le diapositive potrebbero non essere sincronizzate con il video reale 🤷‍♂️

Mi piace molto questa parte della trascrizione:

Perché questo sta testando ... vogliamo assicurarci che venga chiamata la getfunzione da Gettable, perché può tornare e la funzione potrebbe teoricamente assegnare una serie di prodotti alimentari da qualsiasi luogo . Dobbiamo assicurarci che sia chiamato;


3
Grande esempio, aggiungerei semplicemente che in questo esempio particolare la sottoclasse funge da finto, ma in questo esempio viene utilizzata anche la stub. Le risposte JSON codificate sono considerate risposte stub. Aggiungo questo solo perché può essere difficile distinguere tra beffe e tronconi, ma questo esempio mostra chiaramente come entrambi possano essere usati insieme.
user3344977,

Ottima spiegazione, grazie. Una piccola modifica alla domanda sulla modifica dell'API. Che cosa succede se non è la tua API, quindi non fai parte del processo di sviluppo? Voglio sapere quando la mia libreria client non funziona.
ThinkDigital,

@ThinkDigital I buoni fornitori di API hanno buone note di rilascio e comunicano correttamente le modifiche, se non si dispone di quel canale, forse è il momento di sedersi in una riunione e discuterne | i buoni sviluppatori esamineranno sempre le modifiche dell'API di una nuova versione ed eviteranno semplicemente di aggiornare la versione dell'API. Hai versioni API? Se nessuno di questi lo rileva, scoprirai il QAing, quindi aggiorna i tuoi test ← dovere di tutto il team. → dovere un singolo sviluppatore: non dovrebbe importare molto. Gestisci il caso in cui il server restituisce un errore o il server non restituisce un errore ma non è in grado di analizzare JSON o gestisce il caso corretto.
Miele

Grazie per aver risposto, @Honey! Nel mio caso, sto mantenendo un client per pub.dev , che ha un'API, ma è gravemente carente. Tanto che era meglio creare un'API raschiando il loro sito, piuttosto che usare l'API ufficiale. Per questo motivo, le modifiche al sito possono violare il codice e non dovrebbero preoccuparsi di aggiornare nessuno in questo caso. Il sito è open source, ma è diverso mantenere un'API basata su modifiche apportate su una base più banale.
ThinkDigital,

32

Ci sono molte risposte su SO e buoni post sul web sul deridere. Un posto in cui potresti iniziare a cercare è il post di Martin Fowler Mocks Aren't Stubs in cui discute molte idee di derisione.

In un paragrafo - Il derisione è una tecnica particolare per consentire il test di un'unità di codice senza dipendere da dipendenze. In generale, ciò che differenzia la derisione dagli altri metodi è che gli oggetti simulati utilizzati per sostituire le dipendenze del codice consentiranno di impostare le aspettative: un oggetto simulato saprà come deve essere chiamato dal codice e come rispondere.


La tua domanda originale menzionava TypeMock, quindi ho lasciato la mia risposta a quella qui sotto:

TypeMock è il nome di un framework di derisione commerciale .

Offre tutte le funzionalità dei framework di derisione gratuiti come RhinoMocks e Moq, oltre ad alcune opzioni più potenti.

Indipendentemente dal fatto che TypeMock sia o meno altamente discutibile, puoi fare la maggior parte delle derisioni che avresti mai desiderato con librerie di derisione gratuite, e molti sostengono che le capacità offerte da TypeMock spesso ti porteranno lontano da un design ben incapsulato.

Come ha affermato un'altra risposta, "TypeMocking" non è in realtà un concetto definito, ma potrebbe essere inteso come il tipo di derisione offerto da TypeMock, utilizzando il profiler CLR per intercettare le chiamate .Net in fase di esecuzione, offrendo una capacità molto maggiore di falsificare oggetti (non requisiti come bisogno di interfacce o metodi virtuali).


@Masoud non ha mai menzionato TypeMock. La sua domanda riguardava il "tipo beffardo" in generale.
Peter Lillevold,

4
@Peter - come ha detto un altro commento, controlla la cronologia delle modifiche della domanda. Non posso fare molto se invio una risposta e la domanda originale viene completamente cambiata.
David Hall,

9

Il finto è un metodo / oggetto che simula il comportamento di un metodo / oggetto reale in modo controllato. Gli oggetti simulati vengono utilizzati nel test unitario.

Spesso un metodo sottoposto a un test chiama altri servizi o metodi esterni al suo interno. Queste sono chiamate dipendenze. Una volta derise, le dipendenze si comportano nel modo in cui le abbiamo definite.

Con le dipendenze controllate da beffe, possiamo facilmente testare il comportamento del metodo che abbiamo codificato. Questo è il test unitario.

Qual è lo scopo degli oggetti finti?

Mock vs stub

Test unitari vs test funzionali


7

Il derisione sta generando pseudo-oggetti che simulano il comportamento di oggetti reali per i test


5

Lo scopo dei tipi beffardi è di separare le dipendenze al fine di isolare il test su un'unità specifica. I mozziconi sono surrogati semplici, mentre i finti sono surrogati che possono verificarne l'utilizzo. Un framework beffardo è uno strumento che ti aiuterà a generare stub e beffe.

EDIT : Dal momento che la formulazione originale menziona "il tipo di derisione" ho avuto l'impressione che ciò si riferisse a TypeMock. Nella mia esperienza il termine generale è semplicemente "beffardo". Non esitate a ignorare le informazioni di seguito specificatamente su TypeMock.

TypeMock Isolator differisce dalla maggior parte degli altri framework di derisione in quanto funziona al volo con la mia modifica di IL. Ciò gli consente di deridere tipi e istanze che la maggior parte degli altri framework non può deridere. Per deridere questi tipi / istanze con altri framework è necessario fornire le proprie astrazioni e deriderle.

TypeMock offre una grande flessibilità a spese di un ambiente di runtime pulito. Come effetto collaterale del modo in cui TypeMock raggiunge i suoi risultati, a volte otterrai risultati molto strani quando usi TypeMock.


@Masoud non ha mai menzionato TypeMock. La sua domanda riguardava il "tipo beffardo" in generale.
Peter Lillevold,

1
@Peter: la formulazione originale era "cos'è il tipo beffardo?".
Brian Rasmussen,

Lo so. Poiché "il tipo di derisione" non equivale a "TypeMock", trovo la risposta sia tua che @Oded abbastanza fuori dal segno.
Peter Lillevold,

1
@Peter: Nella mia esperienza il termine generale è "beffardo", ma in ogni caso ho aggiornato la mia risposta per renderlo più chiaro. Grazie per l'input.
Brian Rasmussen,

3

Penso che l'uso del framework di derisione dell'isolatore TypeMock sarebbe TypeMocking.

È uno strumento che genera simulazioni da utilizzare nei test unitari, senza la necessità di scrivere il codice tenendo presente l'IoC.


@Masoud non ha mai menzionato TypeMock. La sua domanda riguardava il "tipo beffardo" in generale.
Peter Lillevold,

3
In realtà, la domanda originale includeva la parola "Tipo" prima di "Deridere", ma è stata successivamente modificata. Ecco perché alcune delle risposte contengono informazioni specifiche su TypeMock.
Martin Liversage,

1

Se il tuo finto implica una richiesta di rete, un'altra alternativa è quella di avere un vero server di test da colpire. È possibile utilizzare questo servizio per generare una richiesta e una risposta per i test. http://testerurl.com/


Ho appena provato ad accedervi e ci sono voluti diversi minuti. Chi può dire che non sta registrando segretamente anche le richieste? Infine, potrebbe essere meglio come commento :)
Kieren Johnstone,

In realtà l'ho rimosso dal momento che non mi andava di spostarlo in un hosting gratuito. sì, questo avrebbe dovuto essere un commento. è open source, quindi se ci sono dubbi sulla registrazione delle richieste, è possibile eseguirne una propria. github.com/captainchung/TesterUrl
Matthew Chung
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.