2012-12-06 7 views
8

Ich verwende GWTP, indem ich eine Vertragsebene hinzufüge, um das Wissen zwischen Presenter und View zu abstrahieren, und ich bin ziemlich zufrieden mit dem Ergebnis mit GWTP. Ich teste meine Moderatoren mit Mockito.Testen von GWTP-Presenter mit asynchronen Aufrufen

Aber als Zeit verging, fand ich es schwierig, einen sauberen Presenter mit seinen Tests zu halten. Es gibt einige Refactoring-Sachen, die ich getan habe, um das zu verbessern, aber ich war immer noch nicht zufrieden.

Ich fand Folgendes, um das Herz der Sache zu sein: Meine Moderatoren benötigen oft asynchronen Aufruf, oder rufen Sie im Allgemeinen auf Objekte Methode mit einem Rückruf, um meine Presenter-Flow (sie sind in der Regel geschachtelt).

Zum Beispiel:

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

In meinen Tests, ich beendete die Bevölkerung() Aufruf des verspottete PopulationManager Objekt zu überprüfen. Dann um einen weiteren Test für die doSomeStufWithTheView() -Methode zu erstellen.

Aber ich entdeckte ziemlich schnell, dass es schlechtes Design war: jede Änderung oder Refactoring endete, um viele meiner Tests zu brechen, und zwang mich, von Anfang an andere zu erstellen, obwohl die Presenter-Funktionalität nicht geändert wurde! Plus Ich habe nicht getestet, ob der Rückruf effektiv war, was ich wollte.

Also habe ich versucht Mockito doAnswer Methode zu verwenden, um meinen Moderator Testfluss nicht brechen:

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)); 

ich den Code einkalkuliert für sie weniger ausführlich zu sein (und intern weniger abhängig zu der ARG-Position):

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

während also die populationManager spöttisch, habe ich noch den Fluss meines Moderatoren, im Grunde wie das testen kann:

@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. 
} 

Ich frage mich, ob es eine gute Anwendung der DoAnswer-Methode ist, oder ob es ein Code-Geruch ist, und ein besseres Design verwendet werden kann?

Normalerweise tendieren meine Moderatoren dazu, andere Objekte (wie ein Mediator-Muster) zu verwenden und interagieren mit der Ansicht. Ich habe einige Präsentatoren mit mehreren hundert (~ 400) Zeilen Code.

Noch einmal, ist es ein Beweis für schlechtes Design, oder ist es normal, dass ein Moderator ausführlich ist (weil er andere Objekte verwendet)?

Hat jemand von einem Projekt gehört, das GWTP verwendet und seinen Presenter sauber testet?

Ich hoffe, ich habe es ausführlich erklärt.

Vielen Dank im Voraus.

PS: Ich bin ziemlich neu in Stack Overflow, plus mein Englisch fehlt noch, wenn meine Frage etwas verbessert werden muss, bitte sagen Sie mir.

Antwort

1

Sie könnten ArgumentCaptor verwenden:
Überprüfen Sie diese blog post für weitere Details.

+0

das ist einfach perfekt !!! +1 – EMM

0

Wenn ich richtig verstanden habe, fragen Sie nach Design/Architektur.

Dies sollte nicht als Antwort gezählt werden, es ist nur meine Gedanken.

Wenn ich gefolgt Code:

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

ich in der Regel nicht zählen auf Testamentsvollstrecker und prüfen nur, dass Methoden konkrete Arbeit durch das Laden funktioniert und zu speichern. Der Executor ist also nur ein Instrument, um lange Operationen im UI-Thread zu verhindern.

Wenn ich so etwas wie:

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

ich zuerst prüfen wird, dass wir für Veranstaltungen abonniert (und unsubscribed auf einige zu zerstören) und würde ich prüfen, ob onAccountEvent Szenarien nicht erwartet.

UPD1. Wahrscheinlich, in Beispiel 1, wäre besser, extrahieren Sie die Methode loadFromServerAndSave und überprüfen Sie, dass es nicht auf UI-Thread als auch überprüft wird, dass es alles wie erwartet tut.

UPD2. Es ist besser, Framework wie Guava Bus für die Verarbeitung von Ereignissen zu verwenden.

0

Wir verwenden dieses DoAnswer-Muster auch in unseren Presenter-Tests und normalerweise funktioniert es gut. Eine Einschränkung: Wenn Sie es so testen, entfernen Sie effektiv die asynchrone Art des Aufrufs, dh der Rückruf wird sofort nach dem Serveraufruf ausgeführt.

Dies kann zu unentdeckten Rennbedingungen führen. Um dies zu überprüfen, können Sie dies in zwei Schritten durchführen: Beim Aufrufen des Servers speichert die Antwortmethode nur den Rückruf. Dann, wenn es in Ihrem Test angemessen ist, rufen Sie etwas wie flush() oder onSuccess() auf Ihre Antwort (ich würde vorschlagen, eine Dienstprogramm-Klasse für diese, die in anderen Umständen wiederverwendet werden können), so dass Sie steuern können, wenn Callback für das Ergebnis wird wirklich aufgerufen.

Verwandte Themen