Arbeite mich gerade in das Testen mit gemockten Objekten ein. Vielleicht kann mir jemand die Frage beantworten. Wie teste ich mit Powermockito die Argumente von void-methoden?
Beispiel:
private final Logger logger = Logger.getRootLogger();
private final ResourceBundle messages = PropertyResourceBundle.getBundle("res.messages", new Locale("en"));
@Override
public void uncaughtException(Thread t, Throwable e) {
logger.error(messages.getString("error.exception.uncaught"));
logger.info(e);
}
}```
Weiter bin ich bisher leider noch nicht gekommen:
```@Test
public void testUncaughtException() {
UncaughtExcHandler ue=new UncaughtExcHandler();
UncaughtExcHandler spy=PowerMockito.spy(ue);
}```
Bekomme ich das mit when...then... hin oder wie würdet ihr das umsetzen?
Die Frage ist, welches verhalten der Metrhode willst Du denn Testen?
Ich vermute mal, Du willst testen, dass die richtige Loggerausgabe gemacht wird.
In dem Fall würde ich das Logger-Objekt mocken und darauf Prüfen, welche Parameter es bekommen hat:
[SPOILER]```@Test
public void testUncaughtException_withThrowable_passesToLogger(){
Logger logger = mock(Logger.class);
Throwable throwable = mock(Throwable);
UncaughtExcHandler ue=new UncaughtExcHandler(loggerMock);
Guter Tip soweit,
ich glaube nur ich benötige doch Powermock um den Logger im ‘ue’ Objekt zu setzen ...UncaughtExcHandler ue=new UncaughtExcHandler(loggerMock);...
Das funktioniert nämlich nicht so einfach
[quote=bygones]ch wuerde aber davon absehen nun jeder Klasse, die einen Logger hat nun diesen per Konstruktor zu injezieren.[/quote]Warum?
DI ist ganz allgemein eine als sinvoll erachtete Methode, warum soll man für Logger hier eine Ausnahme machen?
Und streng genommen muss man ja auch in jeder Klasse, die loggt auch testen, dass sie das Richtige ausgibt, womit man ja das Problem sowieso mit jeder dieser Klassen hätte…
Und wenn man dann auch noch eines der DI-Frameworks einsetzt hat man womöglich gar kein Problem mehr…
So streng sehe ich das nicht. Ich teste die Logausgaben nicht. Wenn da was schiefläuft, ist das meistens nicht soo schlimm. Diese Abhängigkeit zu injizieren macht den benutzenden Code nur hässlicher. Zumal der Logger auch klassenspezifisch konfiguriert wird.
Wenn ich also an verschiedenen Stellen Instanzen erzeuge (warum auch immer), dann müsste ich an jeder Stelle das selbe (z. B. LoggerFactory.getLogger(MyClass.class)) injizieren, was dem DRY-Prinzip widerspricht, statt eine Zeile in der Klasse zu schreiben (dank Live-Template sind das bei mir genau 4 Tastendrücke): private final Logger log = LoggerFactory.getLogger(MyClass.class);
Wenn man die Logausgaben nicht testet, dann hat man auch keine Probleme mit Mocking.
[quote=cmrudolph]Wenn ich also an verschiedenen Stellen Instanzen erzeuge (warum auch immer), dann müsste ich an jeder Stelle das selbe (z. B. LoggerFactory.getLogger(MyClass.class)) injizieren, was dem DRY-Prinzip[/quote]es sei den, Du übergibst eine eigene LoggerFactory, bei der getLogger(MyClass.class); eben nicht static ist, dann kann man über deren Mock Logger-Mocks einstreuen. Dieser Verstoß gegen das “Law of Demeter” wäre IMHO tolerierbar.
[quote=cmrudolph;101761]Wenn man die Logausgaben nicht testet, dann hat man auch keine Probleme mit Mocking.[/quote]wohl war.
[QUOTE=Timothy_Truckle;101764]es sei den, Du übergibst eine eigene LoggerFactory, bei der getLogger(MyClass.class); eben nicht static ist, dann kann man über deren Mock Logger-Mocks einstreuen.[/QUOTE]Das ist richtig. Wenn ich das richtig sehe, könnte man das mit slf4j sogar so machen, indem man eine ILoggerFactory übergibt. Das wäre zwar erst einmal ungewohnt, aber sicherlich praktikabel. Im aufrufenden Code dann jeweils LoggerFactory.getILoggerFactory() stehen zu haben, könnte man als Boilerplate abtun. Im Gegenzug erhält man hervorragende Testbarkeit und vermeidet die Codeduplikation bestmöglich.
Wenn man mit einem Setter für die Factory arbeitet und eine Defaultimplementierung wählt (eben die oben genannte), dann kann man die Duplikation sogar noch weiter reduzieren.
[QUOTE=Timothy_Truckle;101764]wohl war.[/QUOTE]Ich gebe zu, das klang jetzt ziemlich blöd, weil so offensichtlich. Was ich damit sagen wollte war, dass es eben darauf ankommt, ob man in diesem Fall bereit ist, Kompromisse einzugehen oder nicht.