Mockito: Wie zum Hohn eine Schnittstelle der JodaTime
Benutze ich JodaTime#DateTime
, und ich müssen zu verspotten sein Verhalten. Da es nicht möglich ist, direkt zu mock JodaTime#DateTime
erstelle ich eine Schnittstelle von it
Clock.java
public interface Clock {
DateTime getCurrentDateTimeEST();
DateTime getFourPM_EST();
DateTime getSevenPM_EST();
}
JodaTime.java
public class JodaTime implements Clock {
@Override
public DateTime getCurrentDateTimeEST() {
return new DateTime(DateTimeZone.forID("EST"));
}
@Override
public DateTime getFourPM_EST() {
DateTime current = getCurrentDateTimeEST();
return new DateTime(current.getYear(), current.getMonthOfYear(),
current.getDayOfMonth(), 16, 0, 0, 0, DateTimeZone.forID("EST"));
}
@Override
public DateTime getSevenPM_EST() {
DateTime current = getCurrentDateTimeEST();
return new DateTime(current.getYear(), current.getMonthOfYear(),
current.getDayOfMonth(), 19, 0, 0, 0, DateTimeZone.forID("EST"));
}
}
Hier ist die Methode, die ich testen möchte
public class PrintProcessor{
Clock jodaTime;
public PrintProcessor(){
jodaTime = new JodaTime();
}
...
public String getPrintJobName(Shipper shipper){
String printJobName = null;
//Get current EST time
if(jodaTime.getCurrentDateTimeEST().isBefore(jodaTime.getFourPM_EST()) ||
jodaTime.getCurrentDateTimeEST().isAfter(jodaTime.getSevenPM_EST())){ //Before 4PM EST and after 7PM EST
switch(shipper){
case X:
...
}else if(jodaTime.getCurrentDateTimeEST().isBefore(jodaTime.getSevenPM_EST())){ //Between 4PM-7PM EST
switch(shipper){
case X:
...
}
return printJobName;
}
}
Wie Sie sehen können die printJobName
sind abhängig von der aktuellen Zeit des Tages relativ zum Zeitintervall [4PM-7PM] EST und den Namen des Versenders. Da der Absender sein pass über die parameter können wir die unit-Tests ist es kein problem. Aber ich müssen zu verspotten die Zeit. So, hier ist was ich versuchen
@Test
public void testGetPrintJobNameBeforeFourPM(){
DateTime current = new DateTime(DateTimeZone.forID("EST"));
Clock clock = mock(Clock.class);
//Always return 6pm when I try to ask for the current time
when(clock.getCurrentDateTimeEST()).thenReturn(new DateTime(current.getYear(), current.getMonthOfYear(),
current.getDayOfMonth(), 18, 0, 0, 0, DateTimeZone.forID("EST")));
//Test for Fedex
String printJobName = printProcessor.getPrintJobName(Shipper.X);
assertEquals("XNCRMNCF", printJobName);
}
Sollte der test scheitern, da ich den pass in 6PM, aber XNCRMNCF
ist der name für die vor 4 Uhr. Muss ich mock printProcessor
als gut. Wenn das, was ich habe, ist falsch. Wie soll ich es beheben? Ich bin versucht zu lernen, schreiben von high-level-java-code, bitte sehr kritisiert zu meinem code. Ich möchte wirklich lernen
InformationsquelleAutor Thang Pham | 2011-05-18
Du musst angemeldet sein, um einen Kommentar abzugeben.
Dies ist ein klassischer Fall von Tests zeigt sich ein möglicher Fehler im design. Sie können nicht spotten
JodaTime
da haben Sie eine hard-wired dependency diesen Klassen in Ihrem class-under-test.Haben Sie einen Blick auf die SOLID Prinzipien zu verstehen, warum dies ein problem sein könnte (vor allem in diesem Fall die Dependency-Inversion-Prinzip). Wenn Sie injiziert
JodaTime
irgendwo als Abhängigkeit, die sich dann in Ihrem Gerät testen, Sie wäre in der Lage, Sie zu ersetzen eine echte instace von es mit eine mock und stub oder spy als angemessen.Jedoch:
JodaTime
ist etwas, das sehr unwahrscheinlich ist, dass injiziert werden, die mit nichts in der Produktionsumgebung, egal wie lange es lebt. Stattdessen wird in diesem Fall würden Sie wahrscheinlich besser bedient, mit der Komponiert Method Design Pattern. Hier würden Sie extrahieren, was die Berechnung/Algorithmus, den Sie verwenden, um zu generieren, dieprintjobName
zu einer anderen Methode (die ich nicht sehen kann, wie Sie es hier tun, weil Ihr code-snippet, das niemals einen Wert zuweist, variable). Dann können Sie Spion (Teil-mock) Ihre Klasse unter test nur spotten, dass Methode und geben einen festen Wert, unabhängig von der real date Zeit, dassJodaTime
liefert, zum Beispiel:Nun können Sie schreiben in Ihrem test:
Mann. In diesem Fall, dann habe ich nicht einmal eine Schnittstelle zu erstellen, der JodaTime an alle, hah. Große Lösung. Danke.
Eigentlich ist dies mehr ein "klassischer Fall" zeigt die Grenzen der einige mocking-tools, eher als eine design-Problem im code geprüft werden. In der Tat, instanziieren
JodaTime
und die Zuordnung zu einerClock
Feld im Konstruktor der client-Klasse ganz natürlich zu sein scheint hier. Es gibt Spott-APIs (einschließlich PowerMock und eine andere, die ich erstellt) , kann Modell eine Klasse wieJodaTime
in einer situation wie dieser.Ich bin nicht einverstanden. neue JodaTime() übernimmt die Aufgabe, eine harte Abhängigkeit. Während mocking-frameworks wie PowerMock kann dies tun, haben Sie einen Blick auf, wie Sie umgesetzt werden. Sie Durcheinander herum mit dem bytecode. Das Endergebnis ist, dass, obwohl Sie sind gut und nützlich, Sie nicht wirklich testen, den code, den Sie wollen, getestet. Sie haben sich bereits geändert. In der Regel ist dies kein problem, aber ich würde nicht raten, mit PowerMock für etwas anderes als die Erlaubnis für Sie, um legacy-code im test. Wenn Sie es für den neuen code, noch der PowerMock-Entwickler wird sagen, du machst es falsch.
InformationsquelleAutor Alan Escreet
Sind Sie nie geben die
PrintProcessor
Ihrer zu spotten. Machen Sie ein mock Objekt ist nicht das gleiche wie geben Sie Ihre mock zu einem Objekt. So, wenn Sie rufen Methoden aufPrintProcessor
, es ist das Betriebssystem auf einer realen Instanz derJodaTime
. Es gibt einige Möglichkeiten zu geben, diePrintProcessor
Ihre mock:Clock
ObjektwhenNew(JodaTime.class).withNoArguments().thenReturn(mockJodaTime);
Dies wird fügen Sie Ihren verspotten, wo immer ein no-arg-Konstruktor fürJodaTime
verwendet wird. Hinweis: Dies wird erfordern, dass Sie ein mock derJodaTime
Klasse.Clock jodaTime
Feld (die, wenn Sie nur definiert im Konstruktor sollte wohlfinal
).Clock
Klasse und einfach zurück, während die mock-tests (die Sie verwenden können, PowerMockito zu verspotten statische Methoden).Clock
parameter und übergeben Sie Ihre mock.2
und4
, ich habe eine Frage für Sie. Vielleicht ist es ein bisschen dumm. In JodaTime, um die aktuelle DateTime, Sie tun DateTime dateTime = new DateTime(). Aber was passiert, wenn ich nicht verwenden, dieses dateTime-Objekt sofort. Lassen Sie uns sagen, ich habe einen Prozess, der dauerte 5 Minuten laufen. Wenn ich Vergleiche die Zeit nach diesem langen Prozess, wird die Zeit um 5 Minuten, irgendwie dateTime spiegeln noch heute die richtige Zeit?Pham In den code bis oben aufgeführt, die Sie instanziieren die
DateTime
Objekte auf Wunsch je Aufruf der Methode,, welche ist die richtige Sache zu tun ist (und was das interface ist implizit anfordern). Dies führt zu der erwarteten Zeit erscheinen (so, 5 Minuten später, Sie werden sehen, die richtige Zeit). Wenn du wirklich nur die Instanziierung einer einzelnen Instanz vonDateTime
für jedenJodaTime
dann werden Sie mit einem problem konfrontiert.InformationsquelleAutor pickypg
Ich denke, du bist definitiv auf dem richtigen Weg. Erstellen Sie die Clock-Schnittstelle für Spott ist definitiv eine gute Idee.
Eine Sache, die sehe ich nicht in deinem code: die Injektion der verspottet Uhr in den printProcessor. Nach der Erzeugung der mock, ich glaube, Sie brauchen etwas entlang der Linien von:
(dies geht, bevor Sie anrufen getPrintJobName. Dieser setter sollte der jodaTime-Eigenschaft in Ihre PrintProcessor Klasse)
Ich bin es gewohnt, EasyMock, so dass ich könnte falsch sein, aber ich bin mir ziemlich sicher, Sie müssen auch die Erwartungen für die getFourPM_EST und getSevenPM_EST Anrufe.
DateTime dateTime = new DateTime()
. Aber was passiert, wenn ich nicht verwenden, diesedateTime
Objekt sofort. Lassen Sie uns sagen, ich habe einen Prozess, der dauerte 5 Minuten laufen. Wenn ich Vergleiche die Zeit nach diesem langen Prozess, wird die Zeit um 5 Minuten, irgendwiedateTime
spiegeln noch heute die richtige Zeit?InformationsquelleAutor Peter