2013-10-09 4 views
8

Ist es ein Code-Geruch, um ein Objekt, das getestet wird, auszuspionieren? Zum Beispiel habe ich eine Klasse LineCounter, deren Aufgabe es ist, einfach die Anzahl der Zeilen in einer Zeichenfolge zu zählen. -Mockito Spy'ing auf dem zu testenden Objekt

class LineCounter { 
    public int getNumLines(String string) { 
     String metadata = getStringMetadata(string); 

     // count lines in file 
     return numLines; 
    } 

    /** Expensive operation */ 
    protected String getStringMetadata(String string) { 
     // do stuff with string 
    } 
} 

Jetzt mag ich einen JUnit 4 Test schreiben, für diese die getNumLines Methode zu testen, während des teueren getStringMetadata Anruf spöttisch aus. Ich beschließe, Mockitos Spionage-Mechanismus zu verwenden, um getStringMetadata einen Dummy-Wert zurückzugeben.

class LineCounterTests { 
    @Test public void testGetNumLines() { 
     LineCounter lineCounterSpy = Mockito.spy(new LineCounter()); 

     // Mock out expensive call to return dummy value.    
     Mockito.when(lineCounterSpy.getStringMetadata(Mockito.anyString()).thenReturn("foo"); 

     assertEquals(2, lineCounterSpy.getNumLines("hello\nworld"); 
    } 
} 

Ist das eine vernünftige Sache zu tun? Ich fühle mich ziemlich komisch beim Testen eines Spy-Objekts anstatt der eigentlichen Klasse, aber ich kann mir nicht wirklich einen Grund dafür vorstellen.

+4

Dies könnte ein Testfall sein, der zu einer Verbesserung Ihres Codes führt. Es sieht so aus, als ob die Aufgabe, String-Metadaten zu erhalten, in eine andere Klasse extrahiert werden sollte, an die dann 'LineCounter' delegiert wird. An diesem Punkt haben Sie eine "Naht" erstellt und können diese Abhängigkeit auf eine konventionellere (und weniger stinkende) Weise verspotten. – millhouse

+0

@millhouse Ja, ich stimme völlig zu, dass dieser Weg wie der richtige Weg aussieht. Ich frage mich jedoch, gibt es irgendetwas an sich falsch mit meinem Beispiel, wo das zu testende Objekt ein Spion ist? – Matthew

Antwort

3

Ich werde die Frage in zwei Teilen beantworten. Erstens, es ist Code-Geruch, um die getestete Klasse zu verspotten oder auszuspionieren. Das bedeutet nicht, dass es nicht korrekt durchgeführt werden kann, sondern dass es risikoanfällig ist und möglichst vermieden werden sollte.

WRT Ihr spezifisches Beispiel, ich würde sehen, wie der Spion könnte richtig verwendet werden, aber das würde auf der Behauptung, die Sie woanders vollständig Einheit getestet haben getStringMetadata. Dies wirft dann die Frage auf, ob Sie die Unit getStringMetadata woanders komplett getestet haben, dann müssen Sie wissen, wie man es testet und warum nicht getNumLines ohne den Spion testen.

All dies wird gesagt, millhouse macht einen guten Punkt, aber so oder so müssen Sie den teuren Code irgendwo testen. Sein Vorschlag hilft Ihnen, den teuren Code zu isolieren und sicherzustellen, dass Sie ihn nur einmal testen müssen.

+0

Angenommen, ich habe 'getStringMetadata' an anderer Stelle getestet. Ich würde immer noch 'getNumLines' testen wollen, ohne den teuren Aufruf von 'getStringMetadata' durchführen zu müssen, da ich bereits weiß, dass sich diese Methode wie erwartet verhält. Der Vorschlag von @ Millhouse ist gültig, und ich mag den Ansatz, 'getStringMetadata' in eine andere Klasse aufzuteilen, aber meine ursprüngliche Frage bleibt bestehen. Gibt es irgendwas * falsch * bei meinem Beispiel.Oder vielleicht ist es eine bessere Art, das zu fragen, warum man "getStringMetadata" in eine andere Klasse extrahiert und diese Abhängigkeit verspottet, wenn man "LineCounter" einen besseren Ansatz testet? – Matthew

+0

Sie erwähnen auch, dass das Ausspionieren der zu testenden Klasse risikoträchtig ist. Können Sie das näher ausführen? – Matthew

+0

Sie haben gefragt, ob es "Code-Geruch" hat. Ich schlage vor, dass es so ist. Sobald jemand es sieht, denken sie "Warte, was hier vor sich geht". Das bedeutet nicht, dass es nicht richtig gemacht werden kann, nur dass es im Allgemeinen vermieden werden sollte und so die Aufmerksamkeit eines Rezensenten erregen wird. –

0

In dieser Situation ist es vollkommen legitim, die Methode, die von der zu testenden Methode aufgerufen wird, zu stubben. Es ist sogar die einzige Möglichkeit, um es isoliert zu testen. Sie wollen einfach keine einzige Methode in ihre eigene Klasse extrahieren, um sie zu testen.

Vorsicht vor den Nebenwirkungen in der Stubbed-Methode. Es reicht möglicherweise nicht aus, den zurückgegebenen Wert zu stubben, wenn die Stub-Methode Nebenwirkungen hat, dann müssen Sie auch die Nebenwirkungen löschen. In einigen Situationen, in denen die Nebenwirkungen sehr komplex sind, könnte es sogar ein Grund dafür sein, aber das wäre höchstwahrscheinlich ein Anzeichen für einen Code-Geruch bei der Implementierung der getesteten Klasse.

Um Ihre Frage zu beantworten, ich finde es einfach für es Gründe zu finden, aber schwer Gründe zu finden, gegen es. Es ist die Technik, die ich jeden Tag benutze, sie hilft mir, meine Implementierung in kleine Methoden aufzuspalten, die individuell in vollständiger Isolation getestet werden, und ich habe bis jetzt keine Einschränkung gesehen.

Verwandte Themen