2010-06-07 12 views
21

Questa NON è una domanda su quale sia la migliore struttura, ecc.Come funzionano i framework di simulazione di Java?

Non ho mai usato un quadro di derisione e sono un po 'perplesso dall'idea. Come fa a sapere come creare l'oggetto falso? È fatto in fase di runtime o genera un file? Come conosci il suo comportamento? E, cosa più importante, qual è il flusso di lavoro dell'utilizzo di tale framework (qual è il passo passo per la creazione di un test)?

Qualcuno può spiegare? Puoi scegliere, ad esempio, il quadro che ti piace, basta dire di cosa si tratta.

+2

Forse non avresti voluto le risposte "migliore quadro", ma è quello che stai per ottenere ... – skaffman

+0

Vedi anche http://stackoverflow.com/questions/2277039/how-do-mock-frameworks-work , anche se le risposte sono molto tecniche. –

risposta

8

Parlerò del framework che uso (jmock), ma altri fanno qualcosa di molto simile.

Come fa a sapere come creare l'oggetto fittizio ?

Non è così, si dice al framework che si richiede un oggetto fittizio e si dà un tipo (idealmente un'interfaccia) dell'oggetto che si desidera simulare. Dietro le quinte, il framework mock utilizza una combinazione di riflessioni e, a volte, riscrive codice byte per creare un oggetto fittizio.

È eseguito in runtime o genera un file ?

jMock lo crea in fase di esecuzione.

Come fai a sapere il suo comportamento?

Gli oggetti falsi sono piuttosto stupidi. Tu specifichi il comportamento che ti aspetti da loro.

E, soprattutto - ciò che è il flusso di lavoro di utilizzare tale quadro (qual è il passo passo per la creazione di un test).

Una cosa molto importante che il framework deve fornire è la capacità di verificare che il comportamento previsto sia stato osservato durante l'esecuzione del test.

jmock lo fa introducendo un test runner specifico che controlla tutte le aspettative su tutti gli oggetti finti dichiarati dopo che il test è terminato. Se qualcosa non corrisponde, viene generata l'eccezione.

Di solito il modello è:

  1. Acquire fabbrica finto (chiamato Mockery in jMock) dal nulla (con specifico costruttore senza args).
  2. Creare gli oggetti fittizi richiesti.
  3. Setup aspettativa su oggetti mock
  4. chiamare il metodo che si sta testando, che passa prende in giro invece di oggetti reali
  5. Se il metodo restituisce alcuni valori, controllare il valore previsto con regolari assertXXX metodi.
0

EasyMock (http://easymock.org/) è la libreria di derisione che ho usato di più. (Ce ne sono altri: jMock, Mockito, ecc.)

Molti di questi creano un oggetto in fase di esecuzione, che è possibile controllare. Il flusso di base è:

  1. Creare un finto
  2. specificare come dovrebbe agire dicendo quadro finta che cosa chiama aspettarsi. (Questo varia in base al framework.)
  3. Usa la simulazione del test come se fosse la cosa reale
  4. Alcuni framework consentono (o richiedono) di "verificare" che il simulato sia stato usato correttamente.
5

Per una migliore comprensione della derisione, si potrebbe voler creare il proprio oggetto finto. È un processo piuttosto semplice: si crea una classe che implementa la stessa interfaccia di quello che si sta prendendo in giro e che fornisce il comportamento richiesto, registrando le chiamate su un metodo o rispondendo in modo predefinito quando viene chiamato. Da lì inizia a dare più senso il modo in cui vengono create le strutture di derisione.

+0

Immagino che la libreria di finte sappia quale interfaccia usare per riflettere per determinare l'aspetto dell'interfaccia. –

+0

Ho creato oggetti mock prima ... Semplicemente non usando un framework. –

+0

Perché è stato downvoted? È l'unica risposta che affronta la domanda dell'OP. –

14

Un quadro di simulazione prende la ridondanza e lo schema di partenza da una prova di beffa.

Sa creare l'oggetto Mock perché lo dici, naturalmente (a meno che tu non intenda qualcos'altro con quella domanda).

Il modo "standard" per creare un oggetto fittizio è utilizzare/abusare della classe java.lang.reflect.Proxy per creare un'implementazione runtime dell'interfaccia. Questo è fatto in fase di runtime. Il proxy ha una limitazione nel senso che non può proxy classi concrete. Per eseguire il mocking delle classi concrete è necessaria la creazione dinamica di bytecode che crea sottoclassi che annullano la reale implementazione dei metodi pubblici con essenzialmente ciò che verrebbe fatto con un Proxy (registra i parametri del metodo e restituisce un valore predeterminato). Questo ha una limitazione nel fatto che non può sottoclasse le classi finali. Per questo, hai soluzioni come JDave, che (credo, non ho confermato questo) muck con il classloader per rimuovere la designazione finale sulla classe prima di caricarla, quindi la classe non è in realtà finale in runtime il più lontano come la JVM è interessata.

Il framework di Mocking è fondamentalmente basato sull'acquisizione dei parametri e sulla verifica rispetto alle aspettative predeterminate e quindi sulla restituzione di un valore predefinito preconfigurato o ragionevole. Non si comporta in alcun modo particolare, che è il punto. Il codice chiamante viene verificato che chiama il metodo con il parametro corretto e forse come reagisce a valori di ritorno specifici o eccezioni generate. Non si verificano effetti collaterali o real realizzazioni della chiamata sull'oggetto reale.

Ecco un esempio reale da un progetto, utilizzando JMock con JUnit4. Ho aggiunto commenti per spiegare cosa sta succedendo.

@RunWith(JMock.class) //The JMock Runner automatically checks that the expectations of the mock were actually run at the end of the test so that you don't have to do it with a line of code in every test. 
public class SecuredPresentationMapTest { 

private Mockery context = new JUnit4Mockery(); //A Mockery holds the state about all of the Mocks. The JUnit4Mockery ensures that a failed mock throws the same Error as any other JUnit failure. 

@Test 
public void testBasicMap() { 
    final IPermissionsLookup lookup = context.mock(IPermissionsLookup.class); //Creating a mock for the interface IPermissionsLookup. 
    context.checking(new Expectations(){{ //JMock uses some innovative but weird double brace initialization as its standard idom. 
     oneOf(lookup).canRead(SecuredEntity.ACCOUNTING_CONTRACT); 
      //expect exactly one call to the IPermissionsLookup.canRead method with the the enum of ACCOUNTING_CONTRACT as the value. Failure to call the method at all causes the test to fail. 
      will(returnValue(true)); //when the previous method is called correctly, return true; 
    }}); 

    Map<String, Component> map = new SecuredPresentationMap(lookup, SecuredEntity.ACCOUNTING_CONTRACT); 
    //This creates the real object under test, but passes a mock lookup rather than the real implementation. 
    JLabel value = new JLabel(); 
    map.put("", value); 
    assertThat(((JLabel) map.get("")), is(value)); //This ensures that the Map returns the value placed, which it should based on the fact that the mock returned true to the security check. 
    } 
} 

Se la simulazione passata è stata ignorata, il test avrebbe avuto esito negativo. Se la mappa non riesce a restituire il valore inserito, il test fallisce (ovvero JUnit standard).

Ciò che viene testato qui e in un altro test opposto è che, a seconda di ciò che dice l'interfaccia IPermissionsLookup in merito alla sicurezza, la mappa modifica il suo comportamento rispetto a ciò che viene restituito. Questa è la buona base. L'altro test, la finta restituisce false e qualcos'altro è atteso dalla mappa. L'utilizzo della simulazione garantisce che la mappa si basi sul metodo IPermissionsLookup per determinare la posizione di sicurezza e ciò che viene restituito.

+0

Dovrei dire, a causa di JMock, ho appreso dell'esistenza dell'inizializzatore di istanza (doppie parentesi), e in effetti è davvero strano se lo vedi per la prima volta. –

+0

@Alexander, infatti, i test JMock sono l'unica volta che lo uso. Anche se devo ammettere che è un buon idioma in questo caso particolare, penserei ancora che un metodo astratto (come l'AbstractModule di Guice) sarebbe la strada da seguire nel caso generale. – Yishai