Che cosa significa "mockable"?

1

Mi sono imbattuto in questo termine quando ho esaminato i "tratti" di PHP (che sono apparentemente cattivi, poiché, tra le cose, non sono mockable ), ma non riesco a trovare una definizione per questa" mockability ". Potrebbe essere correlato al polimorfismo? Non c'è nemmeno una voce di Wikipedia che potrei trovare!

    
posta Protector one 25.04.2014 - 11:14
fonte

4 risposte

3

Affinché qualcosa possa essere chiamato mockable , devi essere in grado di sostituire la sua implementazione con un'altra implementazione nel contesto di un test unitario, senza influenzare il codice utilizzato durante il normale runtime. Questo è usato per rendere più facile il test, non tirando fuori una tonnellata di dipendenze o eseguendo operazioni che richiedono molte risorse.

Ci sono una serie di errori in quell'articolo. Prima di tutto, quando usi un tratto, dovresti pensarlo come copia e incolla del codice del tratto nella tua classe. Il codice del tratto diventa parte della classe e quindi parte dell'unità che stai testando. Il fatto che i tratti non siano ingannevoli è un po 'ridicolo. È come lamentarsi del fatto che le classi base non siano irrisolvibili.

Se un tratto non ha senso come parte dell'unità, non dovrebbe essere un tratto. Dovresti usare un altro mezzo per condividere la funzionalità, come ereditarietà o composizione. Come ogni altra caratteristica del linguaggio, ci sono situazioni buone e cattive in cui usare i tratti.

In secondo luogo, i tratti sono mockable, sebbene non altrettanto facilmente. In genere dovresti evitarlo, proprio come generalmente eviterai di deridere qualsiasi altro metodo della classe che stai testando. Ad esempio:

trait MyTrait {
    public function traitFunction() {
        expensiveDatabaseCall();
    }
}

class MyClass {
    use MyTrait;
}

Per simulare il tratto, usa qualcosa come il seguente nel tuo test unitario:

trait MockTrait {
    public function traitFunction() {
        echo "don't have to make a database call";
    }
}

class TestMyClass extends MyClass {
    use MockTrait;
}

Questa è una variante della tecnica "sottoclasse e override" di Michael Feathers nel suo libro Funzionante in modo efficace con il codice legacy .

Ancora una volta, la tecnica non è la prima scelta per i test unitari, ma lo farà in un colpo solo. La preferenza sarebbe quella di simulare expensiveDatabaseCall() .

Gli oggetti fittizi vengono solitamente passati come argomento a un costruttore o un metodo. Non puoi passare un tratto come argomento, che sospetto sia il motivo per cui l'autore di quell'articolo li ha definiti non raggianti, ma è lontano dal fatto che i tratti ti impediscano di testare efficacemente una classe con una unità, anche se non ti prendi in giro loro.

In effetti, l'utilizzo corretto dei tratti può semplificare il codice, rendendolo più facile al test unitario. Riducono il boilerplate e riducono il codice "plumbing" che esiste solo per connettere altre classi che fanno il vero lavoro.

    
risposta data 28.04.2014 - 21:37
fonte
6

Huh. L'ho trovato dopo aver esaminato la descrizione di stackexchange del tag "mocking". Dal momento che tu, lettore, potresti essere come me, controlla questo: link

In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior of a human in vehicle impacts.

    
risposta data 25.04.2014 - 11:14
fonte
3

L'uso di oggetti finti nelle unit test è abbastanza comune. Gli oggetti simulati sono, come hai detto, oggetti controllati che imitano il comportamento di alcuni altri oggetti che non vuoi toccare. Un buon esempio potrebbe essere un metodo che scrive qualcosa nel database - invece di utilizzare i record del database effettivo, è molto meglio utilizzare gli oggetti mock per simulare l'adattatore del database e fornire set di risultati fittizi, per non toccare affatto il database ( se si sta inserendo in un database, alla fine del test sarà necessario modificare le tabelle nella loro forma iniziale, ma se si interrompe il test a metà processo il database rimarrebbe influenzato dal test e si otterrebbero incongruenze altre unità potrebbero essere coinvolte più avanti nel test in modo da interrompere il contesto del test unità e così via).

Il problema con i tratti che non sono raggirabili è che non sono oggetti concreti - nella catena ereditaria si trovano da qualche parte tra il bambino e il genitore, fuori dal tuo controllo attraverso il framework di test unitario (senza alterare il codice reale dell'applicazione per fornire funzionalità specifiche, che non penso di dover dire che è assolutamente inutile). Un'analogia un po 'distante sarebbe il pattern di progettazione di un decoratore , dove oggetti particolari sono aumentati in fase di esecuzione con vari altri comportamenti iniettati che possono differiscono dall'implementazione della classe originale - ma nel caso dei decoratori, sai cosa aspettarti e puoi distruggere il contesto dopo che ciascuna unità è stata eseguita.

Poiché un tratto può alterare il contesto degli oggetti (chiamando un metodo implementato nel tratto che esegue operazioni di database, ad esempio, o che ha una caratteristica che sovrascrive una variabile di classe interna dalla classe genitore, che non è controllata nella classe figlio) , è possibile ottenere un comportamento imprevisto che può esistere al di fuori del campo di prova dell'unità e che non è possibile controllare utilizzando una simulazione. Per continuare l'analogia con il crash test, avresti un manichino di crash test che può tirare il freno di stazionamento o slacciare la cintura di sicurezza senza che tu possa controllare quella parte del comportamento.

    
risposta data 25.04.2014 - 13:05
fonte
1

Puoi trovare persone che scrivono libri più o meno da soli su questo argomento (insieme a concetti come "stub", "spy", "fake" o "dummy"). I puritani (scusate l'insulto possibile ad alcune persone qui) discuteranno sulle differenze di suttle tra ad es. mock and stubs (vedere ad esempio l'articolo di Martin Fowlers link )

.

I miei 5 cents sostengono che un mock è un oggetto che imita il comportamento di un oggetto dipendente dell'oggetto sottoposto a test . Puoi fare in modo che il mock restituisca i valori previsti oppure puoi verificare che determinati attributi / proprietà o metodi vengano chiamati come parte del test. Essere derisi allora, in qualche modo, implica un comportamento sostituibile, attraverso un'interfaccia o altro. Ciò consente all'oggetto mock di registrare e sostituire il comportamento.

Sono venuto a scoprire che per i miei scopi di test (.NET) una libreria come NSubstitute ( link ) è giusta. Una delle idee di guida è (citazione): "Mock, stub, falso, spy, test double? Strict o loose? Nah, basta sostituire il tipo che ti serve!"

    
risposta data 28.04.2014 - 21:58
fonte

Leggi altre domande sui tag