2009-07-23 14 views
34

È possibile creare un oggetto fittizio che implementa diverse interfacce con EasyMock?È possibile creare un oggetto fittizio che implementa più interfacce con EasyMock?

Ad esempio, interfaccia Foo e interfaccia Closeable?

In Rhino Mocks è possibile fornire più interfacce durante la creazione di un oggetto fittizio, ma il metodo createMock() di EasyMock richiede solo un tipo.

È possibile raggiungere questo obiettivo con EasyMock, senza ricorrere al fallback della creazione di un'interfaccia temporanea che estenda sia Foo e Closeable, sia che si deridi?

risposta

10

EasyMock non supporta questo modo si è bloccato con fallback dell'interfaccia temporanea.

Per inciso, sento un po 'di codice wiff - un metodo dovrebbe trattare davvero un oggetto come 2 cose diverse, l'interfaccia Foo e in questo caso?

Questo implica che il metodo sta eseguendo più operazioni e mentre sospetto che una di queste operazioni sia quella di "chiudere" lo Closeable, non avrebbe più senso se il codice chiamante decidesse se chiudere o meno ' è obbligatorio?

Strutturando il codice in questo modo mantiene "aperto" e "chiuso" nello stesso blocco try ... finally e IMHO rende il codice più leggibile senza menzionare il metodo più generale e consente di passare oggetti che implementano solo Foo.

+4

Sono d'accordo con questo, ma per espandere: se stai usando Iniezione di dipendenza, e la tua classe ha bisogno sia di un Foo che di un Closable, dovresti davvero avere due setter separati per quelli. Se scegli di iniettare lo stesso oggetto per entrambi, allora è grandioso, ma penso che la classe sotto test non abbia bisogno di sapere che sono lo stesso oggetto - dovrebbe trattare il Foo as a Foo e il Closeable as a Closeable –

+0

Nick, Matt, grazie per il tuo contributo. Giusto per chiarire lo scenario, il contesto è che Foo è un'interfaccia per un sistema modulare aggiuntivo. I moduli di terze parti implementano Foo e vengono quindi istanziati e utilizzati dal framework. Possono anche implementare Opzionalmente Closeable, nel qual caso il framework li chiuderà quando ha finito di usarli. Quindi, i test unitari devono coprire due scenari distinti: un Foo che è anche chiudibile e un Foo che non è Chiudibile. Spero che abbia senso. –

+1

@NickHolt: non sarei completamente d'accordo con te. Considera il caso in cui hai un'interfaccia 'Persona' che ha solo getter (' getFirstName() ',' getAddress() ', ...) e l'interfaccia 'ModifiablePerson', che ha solo setter (' setFirstName() ',' setAddress() ', ...). E ora vuoi testare un SUT, che prende 'Person', ma controlla se ha superato l'oggetto' instanceof ModifiablePerson' e fa qualcosa in base a ciò. 'Closeable' è anche un buon esempio: se l'oggetto fornisce una funzionalità" estesa ", che è esplicitamente controllata da' instanceof' e sfruttata, cosa c'è di male in questo? –

13

Avete preso in considerazione qualcosa di simile:

interface Bar extends Foo, Closeable { 
} 

e quindi finto interfaccia Bar?

+1

Sì, stavo solo vedere se Potrei evitarlo. Alla mia domanda: "È possibile raggiungere questo obiettivo con EasyMock, senza ricorrere al fallback della creazione di un'interfaccia temporanea che estenda sia Foo che Closeable, e poi lo deridi?" –

+0

Mi piace questa soluzione - per creare tale interfaccia nella classe di test è ok per me, buona idea ;-) – Betlista

+0

Non proprio il desiderio dell'OP ma funziona :) –

2

In base alle mie conoscenze, l'unico strumento di simulazione per Java con supporto esplicito per la simulazione di più interfacce è JMockit. (La mia ispirazione per l'aggiunta di questa funzionalità è venuto da Moq e Rhino Mocks, che sono strumenti .NET.)

Un esempio (dal mockit.ExpectationsUsingMockedTest JUnit classe 4 test):


@Test 
public <M extends Dependency & Runnable> void mockParameterWithTwoInterfaces(final M mock) 
{ 
    new Expectations() 
    { 
     { 
     mock.doSomething(true); returns(""); 
     mock.run(); 
     } 
    }; 

    assertEquals("", mock.doSomething(true)); 
    mock.run(); 
} 

Dependency e Runnable sono interfacce. Il metodo doSomething appartiene al primo e run al secondo.

+0

Questo snippet non sembra creare l'oggetto fittizio. Come lo faresti? –

+0

Il test sopra funziona per me. Hai provato a eseguirlo? –

49

Anche se fondamentalmente d'accordo con la risposta di Nick Holt, ho pensato di sottolineare che mockito permette di fare ciò che si chiede con il seguente invito:

Foo mock = Mockito.mock(Foo.class, withSettings().extraInterfaces(Bar.class)); 

Ovviamente dovrete usare il cast: (Bar)mock quando è necessario utilizzare il mock come Bar ma che cast non sarà gettare ClassCastException

Ecco un esempio che è un po 'più completo, anche se del tutto assurda:

import static org.junit.Assert.fail; 
import org.junit.Test; 
import static org.mockito.Mockito.*; 
import org.mockito.Mockito; 
import static org.hamcrest.MatcherAssert.assertThat; 
import static org.hamcrest.Matchers.*; 
import org.hamcrest.Matchers; 

import java.util.Iterator; 


public class NonsensicalTest { 


    @Test 
    public void testRunnableIterator() { 
     // This test passes. 

     final Runnable runnable = 
        mock(Runnable.class, withSettings().extraInterfaces(Iterator.class)); 
     final Iterator iterator = (Iterator) runnable; 
     when(iterator.next()).thenReturn("a", 2); 
     doThrow(new IllegalStateException()).when(runnable).run(); 

     assertThat(iterator.next(), is(Matchers.<Object>equalTo("a"))); 

     try { 
      runnable.run(); 
      fail(); 
     } 
     catch (IllegalStateException e) { 
     } 
    } 
2

Un altro modo per risolvere questo problema è quello di utilizzare un CGLib mixin:

final Interface1 interface1 = mockery.mock(Interface1.class); 
final Interface2 interface2 = mockery.mock(Interface2.class); 

service.setDependence(Mixin.create(new Object[]{ interface1, interface2 })); 

mockery.checking(new Expectations(){{ 
    oneOf(interface1).doSomething(); 
    oneOf(interface2).doNothing(); 
}}); 

service.execute(); 

o meno questa è una buona idea, è qualcosa fino alla discussione ...