2016-06-22 16 views
1

Es wird oft gesagt, dass Schreibeinheit Tests nur eine einzige Klasse testen und alle Mitarbeiter verspotten. Ich versuche, TDD zu lernen, um meinen Codeentwurf besser zu machen, und jetzt bin ich mit einer Situation fest, in der diese Regel gebrochen werden sollte. Oder sollte es nicht?Wie testen Sie, ob das Objekt korrekt erstellt wird, während Sie die zu testende Klasse isoliert halten?

Ein Beispiel: Klasse unter Test eine Methode, die ein Person erhält, erzeugt ein Employee basierend auf den Person und gibt die Employee.

public class EmployeeManager { 

    private DataMiner dataMiner; 

    public Employee getCoolestEmployee() { 
     Person dankestPerson = dataMiner.getDankestPerson(); 
     Employee employee = new Employee(); 
     employee.setName(dankestPerson.getName() + "bug in my code"); 
     return employee; 
    } 

    // ... 
} 

Sollte Employee ein Kollaborateur angesehen werden? Wenn nicht, warum nicht? Wenn ja, wie prüfe ich ordnungsgemäß, ob "Mitarbeiter" korrekt erstellt wurde?

Hier ist der Test, den ich im Sinn haben (mit JUnit und Mockito):

@Test 
public void coolestEmployeeShouldHaveDankestPersonsName() { 
    when(dataMinerMock.getDankestPerson()).thenReturn(dankPersonMock); 
    when(dankPersonMock.getName()).thenReturn("John Doe"); 

    Employee coolestEmployee = employeeManager.getCoolestEmployee(); 
    assertEquals("John Doe", coolestEmployee.getName()); 
} 

Wie Sie sehen, ich habe coolestEmployee.getName() verwenden - Methode der Employee Klasse, die nicht unter Test.

Eine mögliche Lösung, die den Sinn kommt, ist die Aufgabe der Umwandlung Person s in Employee s auf eine neue Methode der Employee Klasse zu extrahieren, so etwas wie

public Employee createFromPerson(Person person);

Bin ich das Problem Grübeln? Was ist der richtige Weg?

+1

Ich kann nicht sehen, was das Problem ist. Dein Test sieht gut aus. Ja, Sie rufen die 'dankPersonMock.getName()' Methode auf. Aber es ist in Ordnung, da es Mock-Objekt ist. –

+0

Aber ich rufe auch 'coolestEmployee.getName()' und 'coolestEmployee' ist kein Spott. Ich werde es auch in der Frage klären. –

+1

Dennoch kann ich hier keine Probleme sehen. Für mich sieht der Test gut aus. –

Antwort

3

Ziel eines Komponententests ist es, schnell und zuverlässig festzustellen, ob ein einzelnes System defekt ist. Das bedeutet nicht, dass Sie die gesamte Welt um sie herum simulieren müssen, sondern dass Sie sicherstellen sollten, dass die von Ihnen verwendeten Mitarbeiter schnell, deterministisch und gut getestet sind.

Datenobjekte - insbesondere POJOs und generierte Wertobjekte - sind tendenziell stabil und gut getestet, mit sehr wenigen Abhängigkeiten. Wie andere schwer-statusbehaftete Objekte neigen sie auch dazu, sehr langwierig zu sein, zu verspotten, weil spöttische Gerüste keine starke Kontrolle über den Zustand haben (z. B. getX sollte n nach setX(n) zurückgeben). Unter der Annahme, dass Employee ein Datenobjekt ist, ist es wahrscheinlich ein guter Kandidat für die tatsächliche Verwendung in Komponententests, vorausgesetzt, dass jede enthaltene Logik gut getestet ist.

Weitere Mitarbeiter in der Regel nicht zu verspotten:

  • JRE Klassen und Schnittstellen. (Nie zum Beispiel eine Liste vortäuschen. Es wird unmöglich zu lesen sein, und Ihr Test wird dafür nicht besser sein.)
  • Deterministische Klassen Dritter. (Wenn Klassen oder Methoden zu final geändert werden, schlägt Ihr Mock fehl; außerdem, wenn Sie eine stabile Version der Bibliothek verwenden, handelt es sich auch nicht um eine fehlerhafte Fehlerquelle.)
  • Stateful-Klassen, nur weil Mocks viel sind Testen von Interaktionen besser als Zustand. Betrachten Sie stattdessen eine Fälschung oder ein anderes Testdoppel.
  • Schnelle und gut getestete andere Klassen, die wenige Abhängigkeiten haben. Wenn Sie Vertrauen in ein System haben und keine Gefahr für den Determinismus oder die Geschwindigkeit Ihres Tests besteht, brauchen Sie sich nicht darüber lustig zu machen.

Was macht das? Nicht deterministische oder langsame Serviceklassen oder Wrapper, die Sie geschrieben haben, die zustandslos sind oder die sich während des Tests nur sehr wenig ändern, und die möglicherweise viele eigene Mitarbeiter haben. In diesen Fällen wäre es schwierig, einen schnellen und deterministischen Test unter Verwendung der tatsächlichen Klasse zu schreiben, so dass es sehr sinnvoll ist, ein Testdoppel zu verwenden - und es wäre sehr einfach, ein solches unter Verwendung eines Mocking-Frameworks zu erstellen.

Siehe auch: Martin Fowler's Artikel "Mocks Aren't Stubs", die über alle Arten von Test verdoppelt zusammen mit ihren Vor- und Nachteilen.

0

Getter, die nur ein privates Feld lesen, sind normalerweise nicht zu testen. Standardmäßig können Sie sich in anderen Tests ziemlich sicher auf sie verlassen. Daher würde ich mich nicht darum kümmern, dankestPerson.getName() in einem Test für EmployeeManager zu verwenden.

Es ist nichts falsch mit Ihrem Test, soweit das Testen geht. Das Design des Produktionscodes könnte anders sein - das Mokieren dankestPerson bedeutet wahrscheinlich, dass es eine Schnittstelle oder eine abstrakte Basisklasse hat, was ein Zeichen von Overengineering sein könnte, insbesondere für eine Geschäftseinheit. Was ich stattdessen tun würde, ist einfach ein neues Person, setzen Sie seinen Namen auf den erwarteten Wert und richten Sie dataMinerMock ein, um es zurückzugeben.

Auch die Verwendung von "Manager" in einem Klassennamen könnte auf mangelnde Kohäsion und zu große Verantwortlichkeiten hinweisen.