2012-12-06 21 views
8

Sto utilizzando GWTP, aggiungendo un livello Contract per astrarre le conoscenze tra Presenter e View e sono abbastanza soddisfatto del risultato con GWTP. Sto testando i miei presentatori con Mockito.Test del presentatore GWTP con chiamate asincrone

Ma col passare del tempo, ho trovato che era difficile mantenere un presentatore pulito con i suoi test. Ci sono alcune cose di refactoring che ho fatto per migliorarlo, ma non ero ancora soddisfatto.

Ho trovato il seguente argomento: I miei relatori hanno spesso bisogno di una chiamata asincrona, o in genere chiamata a un metodo di oggetti con un callback per continuare il flusso del relatore (di solito sono nidificati).

Ad esempio:

this.populationManager.populate(new PopulationCallback() 
    { 
    public void onPopulate() 
    { 
     doSomeStufWithTheView(populationManager.get()); 
    } 
    }); 

Nel mio test, ho finito per verificare la chiamata della popolazione() dell'oggetto PopulationManager deriso. Quindi creare un altro test sul metodo doSomeStufWithTheView().

Ma ho scoperto piuttosto rapidamente che si trattava di un cattivo design: qualsiasi modifica o refactoring ha finito per interrompere molti dei miei test e mi ha costretto a creare da subito altri, anche se la funzionalità del presentatore non è cambiata! Plus Non ho verificato se il callback fosse effettivamente quello che volevo.

così ho cercato di utilizzare Mockito metodo doAnswer a non rompere il mio presentatore flusso di test:

doAnswer(new Answer(){ 
    public Object answer(InvocationOnMock invocation) throws Throwable 
    { 
     Object[] args = invocation.getArguments(); 
     ((PopulationCallback)args[0]).onPopulate(); 
     return null; 
    } 
}).when(this.populationManager).populate(any(PopulationCallback.class)); 

ho scomposto il codice per essere meno prolissa (ed internamente meno dipendente alla posizione arg):

doAnswer(new PopulationCallbackAnswer()) 
    .when(this.populationManager).populate(any(PopulationCallback.class)); 

Così, mentre il beffardo populationManager, ho potuto ancora testare il flusso del mio presentatore, in fondo così:

@Test 
public void testSomeStuffAppends() 
{ 
    // Given 
    doAnswer(new PopulationCallbackAnswer()) 
    .when(this.populationManager).populate(any(PopulationCallback.class)); 

    // When 
    this.myPresenter.onReset(); 

    // Then 
    verify(populationManager).populate(any(PopulationCallback.class)); // That was before 
    verify(this.myView).displaySomething(); // Now I can do that. 
} 

Mi chiedo se sia un buon uso del metodo doAnswer, o se si tratta di un odore di codice e può essere utilizzato un design migliore?

In genere, i miei relatori tendono a utilizzare solo altri oggetti (come alcuni modelli di mediatore) e interagiscono con la vista. Ho qualche presentatore con diverse centinaia (~ 400) righe di codice.

Anche in questo caso, è una prova di progettazione errata o è normale che un presentatore sia prolisso (perché utilizza altri oggetti)?

Qualcuno ha sentito parlare di qualche progetto che utilizza GWTP e verifica il suo presentatore in modo pulito?

Spero di averlo spiegato in modo completo.

Grazie in anticipo.

PS: Sono abbastanza nuovo per Stack Overflow, più il mio inglese è ancora carente, se la mia domanda ha bisogno di qualcosa da migliorare, per favore dimmi.

risposta

1

È possibile utilizzare ArgumentCaptor:
Consulta questo blog post per ulteriori dettagli.

+0

questo è semplicemente perfetto !!! +1 – EMM

0

Se ho capito bene, mi stai chiedendo di design/architettura.

Questo non dovrebbe essere considerato come risposta, sono solo i miei pensieri.

codice Se ho seguito:

public void loadEmoticonPacks() { 
    executor.execute(new Runnable() { 
     public void run() { 
      pack = loadFromServer(); 
      savePackForUsageAfter(); 
     } 
    }); 
} 

io di solito non contare su di esecutore e solo controllare che i metodi di lavoro fa concreta per caricamento e il salvataggio. Quindi l'esecutore qui è solo uno strumento per prevenire lunghe operazioni nel thread dell'interfaccia utente.

Se ho qualcosa di simile:

accountManager.setListener(this); 
.... 
public void onAccountEvent(AccountEvent event) { 
.... 
} 

controllerò prima che abbiamo sottoscritto per gli eventi (e non sottoscritte su alcuni distruggere) e vorrei verificare che onAccountEvent fa scenari attesi.

UPD1. Probabilmente, nell'esempio 1, sarebbe meglio estrarre il metodo loadFromServerAndSave e verificare che non sia eseguito sul thread dell'interfaccia utente e controllare che faccia tutto come previsto.

UPD2. È preferibile utilizzare framework come Guava Bus per l'elaborazione degli eventi.

0

Stiamo utilizzando questo modello doAnswer anche nei nostri test di presentazione e in genere funziona correttamente. Un avvertimento: se lo si verifica in questo modo si sta effettivamente rimuovendo la natura asincrona della chiamata, cioè la richiamata viene eseguita immediatamente dopo l'avvio della chiamata al server.

Questo può portare a condizioni di gara sconosciute. Per controllarli, è possibile effettuare una procedura in due passaggi: quando si chiama il server, il metodo di risposta salva solo la richiamata. Poi, quando è appropriato nel tuo test, chiami sometinh like flush() o onSuccess() sulla tua risposta (ti suggerirei di creare una classe di utilità per questo che può essere riutilizzata in altre circostanze), in modo da poter controllare quando il callback per il risultato è davvero chiamato.

Problemi correlati