Domanda

Mi piacerebbe provare una classe astratta. Certo, posso scrivere manualmente un finta che eredita dalla classe.

Posso fare questo si utilizza un quadro di scherno (sto usando Mockito), invece di mettere a punto manualmente il mio finto? Come?

È stato utile?

Soluzione

Di seguito vi testare le classi astratte senza creare una sottoclasse "reale" let suggerimento -. La Falsa è la sottoclasse

uso Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS), poi deridere tutti i metodi astratti che vengono richiamati.

Esempio:

public abstract class My {
  public Result methodUnderTest() { ... }
  protected abstract void methodIDontCareAbout();
}

public class MyTest {
    @Test
    public void shouldFailOnNullIdentifiers() {
        My my = Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS);
        Assert.assertSomething(my.methodUnderTest());
    }
}

Nota: La bellezza di questa soluzione è che non si fa sono per implementare i metodi astratti, fino a quando non vengono mai invocati

.

A mio parere onesto, questo è più ordinato rispetto all'utilizzo di una spia, dal momento che una spia richiede un'istanza, il che significa che si deve creare una sottoclasse con istanze della classe astratta.

Altri suggerimenti

Se avete solo bisogno di testare alcuni dei metodi concreti senza toccare nessuna delle abstracts, è possibile utilizzare CALLS_REAL_METHODS (vedi di Morten risposta ), ma se il metodo concreto in prova chiama alcuni degli abstract o metodi di interfaccia non implementati, questo non funzionerà - Mockito si lamenterà "Impossibile chiamare vero e proprio metodo sull'interfaccia java ".

(Sì, è un disegno schifoso, ma alcuni quadri, per esempio Tapestry 4, tipo di forza su di voi.)

La soluzione è quello di invertire questo approccio - utilizzare il comportamento finto ordinaria (vale a dire, tutto è deriso / spense) e utilizzare doCallRealMethod() di chiamare in modo esplicito il metodo concreto in prova. Per es.

public abstract class MyClass {
    @SomeDependencyInjectionOrSomething
    public abstract MyDependency getDependency();

    public void myMethod() {
        MyDependency dep = getDependency();
        dep.doSomething();
    }
}

public class MyClassTest {
    @Test
    public void myMethodDoesSomethingWithDependency() {
        MyDependency theDependency = mock(MyDependency.class);

        MyClass myInstance = mock(MyClass.class);

        // can't do this with CALLS_REAL_METHODS
        when(myInstance.getDependency()).thenReturn(theDependency);

        doCallRealMethod().when(myInstance).myMethod();
        myInstance.myMethod();

        verify(theDependency, times(1)).doSomething();
    }
}

Aggiornamento per aggiungere:

Per i metodi non-vuoto, avrete bisogno di utilizzare thenCallRealMethod() invece, ad esempio:

when(myInstance.myNonVoidMethod(someArgument)).thenCallRealMethod();

Altrimenti Mockito si lamentano "stubbing grezzo rilevato."

È possibile ottenere questo utilizzando una spia (utilizzare l'ultima versione di Mockito 1.8+ però).

public abstract class MyAbstract {
  public String concrete() {
    return abstractMethod();
  }
  public abstract String abstractMethod();
}

public class MyAbstractImpl extends MyAbstract {
  public String abstractMethod() {
    return null;
  }
}

// your test code below

MyAbstractImpl abstractImpl = spy(new MyAbstractImpl());
doReturn("Blah").when(abstractImpl).abstractMethod();
assertTrue("Blah".equals(abstractImpl.concrete()));

quadri di scherno sono progettati per rendere più facile per deridere le dipendenze della classe che si sta testando. Quando si utilizza un quadro di scherno per deridere una classe, la maggior parte dei quadri creare dinamicamente una sottoclasse, e sostituire l'implementazione del metodo con il codice per rilevare quando un metodo viene chiamato e restituendo un valore falso.

Durante il test di una classe astratta, si desidera eseguire i metodi non astratti del soggetto in prova (SUT), quindi un quadro di scherno non è ciò che si desidera.

Parte della confusione è che la risposta alla domanda si è collegato al detto a portata di mano-craft un modello che si estende dalla classe astratta. Non la chiamerei una classe un mock. Un finto è una classe che viene utilizzato in sostituzione di una dipendenza, è programmato con le aspettative, e può essere interrogato per vedere se sono soddisfatte le aspettative.

Invece, vi suggerisco di definire una sottoclasse non astratta della vostra classe astratta nel test. Se questo si traduce in troppo codice, quello potrebbe essere un segno che la classe è difficile da estendere.

Una soluzione alternativa potrebbe essere quella di rendere il vostro banco di prova per sé astratto, con un metodo astratto per la creazione del SUT (in altre parole, il banco di prova sarebbe utilizzare il Template Method design pattern).

Provare a usare una risposta personalizzata.

Ad esempio:

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class CustomAnswer implements Answer<Object> {

    public Object answer(InvocationOnMock invocation) throws Throwable {

        Answer<Object> answer = null;

        if (isAbstract(invocation.getMethod().getModifiers())) {

            answer = Mockito.RETURNS_DEFAULTS;

        } else {

            answer = Mockito.CALLS_REAL_METHODS;
        }

        return answer.answer(invocation);
    }
}

Si restituirà il finto per i metodi astratti e chiamerà il metodo vero e proprio per i metodi concreti.

Ciò che veramente mi fa sentire in colpa per beffardo classi astratte è il fatto, che né il costruttore di default YourAbstractClass () viene chiamata (manca super () in finto), né sembra che ci sia alcun modo Mockito a default inizializzare le proprietà finti ( ad esempio proprietà di elenco con ArrayList vuoto o LinkedList).

La mia classe astratta (in pratica il codice sorgente della classe viene generato) non prevede un'iniezione di dipendenza setter per gli elementi della lista, non un costruttore dove si inizializza gli elementi della lista (che ho provato ad aggiungere manualmente).

Solo la classe attributi di inizializzazione di default l'uso: Lista dep1 privato = new ArrayList; Lista dep2 privato = new ArrayList

Quindi non v'è alcun modo per deridere una classe astratta senza usare un'implementazione oggetto reale (per esempio definizione della classe interna in classe unit test, imperative metodi astratti) e spiare l'oggetto reale (che fa corretta inizializzazione campo).

Peccato che solo PowerMock aiuterebbe qui ulteriormente.

Supponendo tue lezioni di prova sono nella stessa confezione (sotto una radice fonte diversa) come le vostre classi in prova si può semplicemente creare il finto:

YourClass yourObject = mock(YourClass.class);

e chiamare i metodi che si desidera testare proprio come si farebbe con qualsiasi altro metodo.

È necessario fornire le aspettative per ogni metodo che viene chiamato con l'aspettativa su eventuali metodi concreti chiamando il metodo eccellente - non so come si farebbe che con Mockito, ma credo che sia possibile con EasyMock

.

Tutto questo sta facendo è la creazione di un'istanza concreta di YouClass e risparmiando lo sforzo di fornire implementazioni vuote di ogni metodo astratto.

Per inciso, spesso mi trovo utile per implementare la classe astratta nel mio test, dove serve come un esempio di implementazione che provo tramite la sua interfaccia pubblica, anche se questo dipende la funzionalità fornita dalla classe astratta.

È possibile estendere la classe astratta con una classe anonima nel test. Per esempio (usando Junit 4):

private AbstractClassName classToTest;

@Before
public void preTestSetup()
{
    classToTest = new AbstractClassName() { };
}

// Test the AbstractClassName methods.

È possibile creare un'istanza di una classe anonima, iniettare la deride e quindi verificare che la classe.

@RunWith(MockitoJUnitRunner.class)
public class ClassUnderTest_Test {

    private ClassUnderTest classUnderTest;

    @Mock
    MyDependencyService myDependencyService;

    @Before
    public void setUp() throws Exception {
        this.classUnderTest = getInstance();
    }

    private ClassUnderTest getInstance() {
        return new ClassUnderTest() {

            private ClassUnderTest init(
                    MyDependencyService myDependencyService
            ) {
                this.myDependencyService = myDependencyService;
                return this;
            }

            @Override
            protected void myMethodToTest() {
                return super.myMethodToTest();
            }
        }.init(myDependencyService);
    }
}

Tenga presente che la visibilità deve essere protected per la proprietà myDependencyService della classe ClassUnderTest astratta.

Whitebox.invokeMethod (..) può essere utile in questo caso.

Mockito permette beffardo classi astratte mediante l'annotazione @Mock:

public abstract class My {

    public abstract boolean myAbstractMethod();

    public void myNonAbstractMethod() {
        // ...
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyTest {

    @Mock(answer = Answers.CALLS_REAL_METHODS)
    private My my;

    @Test
    private void shouldPass() {
        BDDMockito.given(my.myAbstractMethod()).willReturn(true);
        my.myNonAbstractMethod();
        // ...
    }
}

Lo svantaggio è che non può essere utilizzato se avete bisogno di parametri del costruttore.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top