JMockit 101

1. Introduzione

Con questo articolo, inizieremo una nuova serie incentrata sul toolkit beffardo JMockit.

In questa prima puntata parleremo di cos'è JMockit, delle sue caratteristiche e di come i mock vengono creati e utilizzati con esso.

Gli articoli successivi si concentreranno e approfondiranno le sue capacità.

2. JMockit

2.1. introduzione

Prima di tutto, parliamo di cos'è JMockit: un framework Java per deridere gli oggetti nei test (puoi usarlo sia per JUnit che per TestNG).

Utilizza le API di strumentazione di Java per modificare il bytecode delle classi durante il runtime al fine di alterare dinamicamente il loro comportamento. Alcuni dei suoi punti di forza sono la sua espressività e la sua capacità fuori dagli schemi di deridere metodi statici e privati.

Forse sei nuovo su JMockit, ma sicuramente non è dovuto al fatto che è nuovo. Lo sviluppo di JMockit è iniziato nel giugno 2006 e la sua prima versione stabile risale a dicembre 2012, quindi è in circolazione da un po 'di tempo (la versione attuale è 1.24 al momento della stesura dell'articolo).

2.2. Dipendenza da Maven

Innanzitutto, dobbiamo aggiungere la dipendenza jmockit al nostro progetto:

 org.jmockit jmockit 1.41 

2.3. L'espressibilità di JMockit

Come detto prima, uno dei punti di forza di JMockit è la sua espressività. Per creare mocking e definire il loro comportamento, invece di chiamare metodi dall'API mocking, devi solo definirli direttamente.

Ciò significa che non farai cose come:

API.expect(mockInstance.method()).andThenReturn(value).times(2);

Invece, aspettati cose come:

new Expectation() { mockInstance.method(); result = value; times = 2; }

Potrebbe sembrare che sia più codice, ma potresti semplicemente mettere tutte e tre le righe su una sola. La parte veramente importante è che non si finisce con un grande "treno" di chiamate di metodo concatenate. Invece, ti ritroverai con una definizione di come vuoi che si comporti il ​​mock quando viene chiamato.

Se si tiene conto che nella parte risultato = valore si potrebbe restituire qualsiasi cosa (valori fissi, valori generati dinamicamente, eccezioni, ecc.), L'espressività di JMockit diventa ancora più evidente.

2.4. Il modello Record-Replay-Verify

I test che utilizzano JMockit sono suddivisi in tre fasi differenziate: registrazione, riproduzione e verifica.

  1. Nella fase di registrazione , durante la preparazione del test e prima delle invocazioni ai metodi che vogliamo vengano eseguiti, definiremo il comportamento previsto per tutti i test da utilizzare durante la fase successiva.
  2. La fase di replay è quella in cui viene eseguito il codice sotto test. Le invocazioni di metodi / costruttori derisi registrati in precedenza nella fase precedente verranno ora riprodotte.
  3. Infine, in fase di verifica , affermeremo che il risultato del test è stato quello che ci aspettavamo (e che i mock si sono comportati e sono stati utilizzati secondo quanto definito in fase di record).

Con un esempio di codice, un wireframe per un test sarebbe simile a questo:

@Test public void testWireframe() { // preparation code not specific to JMockit, if any new Expectations() {{ // define expected behaviour for mocks }}; // execute code-under-test new Verifications() {{ // verify mocks }}; // assertions }

3. Creazione di mock

3.1. Annotazioni di JMockit

Quando si utilizza JMockit, il modo più semplice per utilizzare i mock è utilizzare le annotazioni. Ce ne sono tre per la creazione di mock ( @Mocked , @Injectable e @Capturing ) e uno per specificare la classe in fase di test ( @Tested ).

Quando si utilizza l' annotazione @Mocked su un campo, creerà istanze derise di ogni nuovo oggetto di quella particolare classe.

D'altra parte, con l' annotazione @Injectable , verrà creata solo un'istanza derisa.

L'ultima annotazione, @Capturing , si comporterà come @Mocked, ma estenderà la sua portata a ogni sottoclasse estendendo o implementando il tipo di campo annotato.

3.2. Passaggio di argomenti ai test

Quando si utilizza JMockit è possibile passare i mock come parametri di test. Questo è abbastanza utile per creare un mock solo per quel test in particolare, come un oggetto modello complesso che richiede un comportamento specifico solo per un test, ad esempio. Sarebbe qualcosa del genere:

@RunWith(JMockit.class) public class TestPassingArguments { @Injectable private Foo mockForEveryTest; @Tested private Bar bar; @Test public void testExample(@Mocked Xyz mockForJustThisTest) { new Expectations() {{ mockForEveryTest.someMethod("foo"); mockForJustThisTest.someOtherMethod(); }}; bar.codeUnderTest(); } }

Questo modo di creare un mock passandolo come parametro, invece di dover chiamare qualche metodo API, ci mostra ancora una volta l'espressibilità di cui parliamo dall'inizio.

3.3. Esempio completo

Per concludere questo articolo, includeremo un esempio completo di un test utilizzando JMockit.

In questo esempio, testeremo una classe Performer che utilizza Collaborator nel suo metodo perform () . Questo metodo perform () riceve un oggetto Model come parametro da cui userà il suo getInfo () che restituisce una String, questa String verrà passata al metodo collaborate () da Collaborator che restituirà true per questo particolare test, e questo valore verrà passato al metodo receive () da Collaborator .

Quindi, le classi testate appariranno così:

public class Model { public String getInfo(){ return "info"; } } public class Collaborator { public boolean collaborate(String string){ return false; } public void receive(boolean bool){ // NOOP } } public class Performer { private Collaborator collaborator; public void perform(Model model) { boolean value = collaborator.collaborate(model.getInfo()); collaborator.receive(value); } }

E il codice del test finirà per essere come:

@RunWith(JMockit.class) public class PerformerTest { @Injectable private Collaborator collaborator; @Tested private Performer performer; @Test public void testThePerformMethod(@Mocked Model model) { new Expectations() {{ model.getInfo();result = "bar"; collaborator.collaborate("bar"); result = true; }}; performer.perform(model); new Verifications() {{ collaborator.receive(true); }}; } }

4. Conclusione

Con questo, concluderemo la nostra introduzione pratica a JMockit. Se vuoi saperne di più su JMockit, resta sintonizzato per articoli futuri.

L'implementazione completa di questo tutorial può essere trovata nel progetto GitHub.

4.1. Articoli della serie

Tutti gli articoli della serie:

  • JMockit 101
  • Una guida a JMockit - Aspettative
  • Utilizzo avanzato di JMockit