Ich habe das auch mehrmals gebraucht. Ich habe unten ein kleines Beispiel zusammengestellt, das Sie an Ihre Bedürfnisse anpassen möchten. Grundsätzlich erstellen Sie Ihre eigenen Appender
und fügen sie dem gewünschten Logger hinzu. Wenn Sie alles sammeln möchten, ist der Root-Logger ein guter Ausgangspunkt, aber Sie können einen spezifischeren verwenden, wenn Sie möchten. Vergessen Sie nicht, den Appender zu entfernen, wenn Sie fertig sind. Andernfalls kann es zu einem Speicherverlust kommen. Unten habe ich es innerhalb des Tests gemacht, aber setUp
oder @Before
und tearDown
oder oder @After
bessere Orte, abhängig von Ihren Bedürfnissen.
Die folgende Implementierung sammelt auch alles in einem List
In-Speicher. Wenn Sie viel protokollieren, können Sie einen Filter hinzufügen, um langweilige Einträge zu löschen oder das Protokoll in eine temporäre Datei auf der Festplatte zu schreiben (Hinweis: LoggingEvent
ist Serializable
, sodass Sie in der Lage sein sollten, die Ereignisobjekte nur zu serialisieren, wenn Ihre Protokollmeldung angezeigt wird ist.)
import org.apache.log4j.AppenderSkeleton;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.apache.log4j.spi.LoggingEvent;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class MyTest {
@Test
public void test() {
final TestAppender appender = new TestAppender();
final Logger logger = Logger.getRootLogger();
logger.addAppender(appender);
try {
Logger.getLogger(MyTest.class).info("Test");
}
finally {
logger.removeAppender(appender);
}
final List<LoggingEvent> log = appender.getLog();
final LoggingEvent firstLogEntry = log.get(0);
assertThat(firstLogEntry.getLevel(), is(Level.INFO));
assertThat((String) firstLogEntry.getMessage(), is("Test"));
assertThat(firstLogEntry.getLoggerName(), is("MyTest"));
}
}
class TestAppender extends AppenderSkeleton {
private final List<LoggingEvent> log = new ArrayList<LoggingEvent>();
@Override
public boolean requiresLayout() {
return false;
}
@Override
protected void append(final LoggingEvent loggingEvent) {
log.add(loggingEvent);
}
@Override
public void close() {
}
public List<LoggingEvent> getLog() {
return new ArrayList<LoggingEvent>(log);
}
}
logger.getAllAppenders()
, dann durchzugehen undappender.setThreshold(Level.OFF)
jeden anzurufen (und sie zurückzusetzen, wenn Sie fertig sind!). Dies stellt sicher, dass die "schlechten" Nachrichten, die Sie generieren möchten, nicht in den Testprotokollen angezeigt werden und den nächsten Entwickler ausflippen.ListAppender<ILoggingEvent>
anstatt einen eigenen benutzerdefinierten Appender zu erstellen.Logger
toorg.apache.logging.log4j.core.Logger
(die Implementierungsklasse für die Schnittstelle) umwandeln, erhalten SiesetAppender()/removeAppender()
erneut Zugriff auf .Hier ist eine einfache und effiziente Logback-Lösung.
Es ist nicht erforderlich, eine neue Klasse hinzuzufügen / zu erstellen.
Es
ListAppender
basiert auf : einem Whitebox-Logback-Appender, in dem Protokolleinträge in einpublic List
Feld eingefügt werden, mit dem wir unsere Aussagen treffen können.Hier ist ein einfaches Beispiel.
Foo Klasse:
FooTest-Klasse:
JUnit-Zusicherungen klingen nicht sehr angepasst, um bestimmte Eigenschaften der Listenelemente geltend zu machen.
Matcher / Assertion-Bibliotheken wie AssertJ oder Hamcrest erscheinen dafür besser:
Mit AssertJ wäre es:
quelle
mock
die Klasse zu testen, die getestet wird. Sie müssen es mit demnew
Operator instanziierenVielen Dank für diese (überraschend) schnellen und hilfreichen Antworten. Sie haben mich auf den richtigen Weg für meine Lösung gebracht.
Die Codebasis, in der ich dies verwenden möchte, verwendet java.util.logging als Protokollierungsmechanismus, und ich fühle mich in diesen Codes nicht zu Hause genug, um dies vollständig in log4j oder in Protokollierungsschnittstellen / -fassaden zu ändern. Aber basierend auf diesen Vorschlägen habe ich eine Julhandler-Erweiterung "gehackt", und das ist ein Vergnügen.
Es folgt eine kurze Zusammenfassung. Erweitern
java.util.logging.Handler
:Natürlich können Sie so viel speichern, wie Sie möchten / wollen / brauchen
LogRecord
, oder sie alle in einen Stapel schieben, bis Sie einen Überlauf erhalten.In Vorbereitung auf den Junit-Test erstellen Sie einen
java.util.logging.Logger
und fügen einen solchen hinzuLogHandler
:Der Aufruf von
setUseParentHandlers()
besteht darin, die normalen Handler zum Schweigen zu bringen, damit (für diesen Junit-Testlauf) keine unnötige Protokollierung erfolgt. Tun Sie alles, was Ihr zu testender Code benötigt, um diesen Logger zu verwenden, führen Sie den Test und assertEquality aus:(Natürlich würden Sie einen großen Teil dieser Arbeit in eine
@Before
Methode verschieben und verschiedene andere Verbesserungen vornehmen, aber das würde diese Präsentation überladen.)quelle
Eine andere Möglichkeit besteht darin, Appender zu verspotten und zu überprüfen, ob eine Nachricht bei diesem Appender protokolliert wurde. Beispiel für Log4j 1.2.x und mockito:
quelle
Tatsächlich testen Sie einen Nebeneffekt einer abhängigen Klasse. Für Unit-Tests müssen Sie dies nur überprüfen
wurde mit dem richtigen Parameter aufgerufen. Verwenden Sie daher ein Mocking-Framework, um den Logger zu emulieren. Auf diese Weise können Sie das Verhalten Ihrer eigenen Klasse testen.
quelle
Das Verspotten ist hier eine Option, obwohl es schwierig wäre, da Logger im Allgemeinen ein privates statisches Finale sind. Das Einstellen eines Mock-Loggers wäre also kein Kinderspiel oder würde eine Änderung der zu testenden Klasse erfordern.
Sie können einen benutzerdefinierten Appender (oder wie auch immer er heißt) erstellen und registrieren - entweder über eine reine Testkonfigurationsdatei oder über die Laufzeit (in gewisser Weise abhängig vom Protokollierungsframework). Und dann können Sie diesen Appender abrufen (entweder statisch, wenn er in der Konfigurationsdatei deklariert ist, oder anhand seiner aktuellen Referenz, wenn Sie ihn zur Laufzeit einstecken) und seinen Inhalt überprüfen.
quelle
Inspiriert von der Lösung von @ RonaldBlaschke habe ich mir Folgendes ausgedacht:
... was Ihnen Folgendes ermöglicht:
Sie könnten es wahrscheinlich dazu bringen, Hamcrest auf intelligentere Weise zu verwenden, aber ich habe es dabei belassen.
quelle
Für log4j2 ist die Lösung etwas anders, da AppenderSkeleton nicht mehr verfügbar ist. Darüber hinaus funktioniert die Verwendung von Mockito oder einer ähnlichen Bibliothek zum Erstellen eines Appenders mit einem ArgumentCaptor nicht, wenn Sie mehrere Protokollierungsnachrichten erwarten, da das MutableLogEvent über mehrere Protokollnachrichten hinweg wiederverwendet wird. Die beste Lösung, die ich für log4j2 gefunden habe, ist:
quelle
Wie von den anderen erwähnt, könnten Sie ein spöttisches Framework verwenden. Damit dies funktioniert, müssen Sie den Logger in Ihrer Klasse verfügbar machen (obwohl ich es vorziehen würde, das Paket privat zu machen, anstatt einen öffentlichen Setter zu erstellen).
Die andere Lösung besteht darin, einen gefälschten Logger von Hand zu erstellen. Sie müssen den gefälschten Logger schreiben (mehr Fixture Code), aber in diesem Fall würde ich die verbesserte Lesbarkeit der Tests gegenüber dem gespeicherten Code aus dem Mocking Framework bevorzugen.
Ich würde so etwas machen:
quelle
Beeindruckend. Ich bin mir nicht sicher, warum das so schwer war. Ich habe festgestellt, dass ich keines der oben genannten Codebeispiele verwenden konnte, da ich log4j2 über slf4j verwendet habe. Das ist meine Lösung:
quelle
Hier ist, was ich für die Rückmeldung getan habe.
Ich habe eine TestAppender-Klasse erstellt:
Dann habe ich im übergeordneten Element meiner Testeinheit für Testeinheiten eine Methode erstellt:
Ich habe eine logback-test.xml-Datei in src / test / resources definiert und einen Test-Appender hinzugefügt:
und fügte diesen Appender dem Stamm-Appender hinzu:
Jetzt kann ich in meinen Testklassen, die sich von meiner übergeordneten Testklasse erstrecken, den Appender abrufen und die letzte Nachricht protokollieren sowie die Nachricht, die Ebene und das Wurfobjekt überprüfen.
quelle
Für Junit 5 (Jupiter) Spring ist die OutputCaptureExtension von Spring sehr nützlich. Es ist seit Spring Boot 2.2 verfügbar und im Spring-Boot-Test- Artefakt verfügbar .
Beispiel (aus Javadoc):
quelle
getOut()
odergetErr()
.Was mich betrifft, können Sie Ihren Test vereinfachen, indem Sie
JUnit
mit verwendenMockito
. Ich schlage folgende Lösung vor:Deshalb haben wir eine gute Flexibilität für Tests mit unterschiedlicher Nachrichtenmenge
quelle
when(appender.isStarted()).thenReturn(true); when(appender.getName()).thenReturn("Test Appender");
und Veränderung Logging -> LogEventquelle
Die API für Log4J2 unterscheidet sich geringfügig. Möglicherweise verwenden Sie auch den asynchronen Appender. Ich habe dafür einen gesperrten Appender erstellt:
Verwenden Sie es so:
quelle
Beachten Sie, dass in Log4J 2.x die öffentliche Schnittstelle
org.apache.logging.log4j.Logger
die MethodensetAppender()
und nicht enthältremoveAppender()
.Wenn Sie jedoch nichts Besonderes tun, sollten Sie es in die Implementierungsklasse umwandeln können
org.apache.logging.log4j.core.Logger
, die diese Methoden verfügbar macht.Hier ist ein Beispiel mit Mockito und AssertJ :
quelle
Eine andere erwähnenswerte Idee, obwohl es sich um ein älteres Thema handelt, ist die Erstellung eines CDI-Produzenten, der Ihren Logger injiziert, damit das Verspotten einfach wird. (Und es bietet auch den Vorteil, dass nicht mehr die "gesamte Logger-Anweisung" deklariert werden muss, aber das ist kein Thema.)
Beispiel:
Erstellen des zu injizierenden Loggers:
Das Qualifikationsspiel:
Verwenden des Loggers in Ihrem Produktionscode:
Testen des Loggers in Ihrem Testcode (anhand eines easyMock-Beispiels):
quelle
Mit Jmockit (1.21) konnte ich diesen einfachen Test schreiben. Der Test stellt sicher, dass eine bestimmte ERROR-Nachricht nur einmal aufgerufen wird.
quelle
Das Verspotten des Appenders kann dabei helfen, die Protokollzeilen zu erfassen. Ein Beispiel finden Sie unter: http://clearqa.blogspot.co.uk/2016/12/test-log-lines.html
quelle
Verwenden Sie den folgenden Code. Ich verwende denselben Code für meinen Federintegrationstest, bei dem ich die Protokollierung für die Protokollierung verwende. Verwenden Sie die Methode assertJobIsScheduled, um den im Protokoll gedruckten Text zu bestätigen.
quelle
Wenn Sie
java.util.logging.Logger
diesen Artikel verwenden, kann er sehr hilfreich sein. Er erstellt einen neuen Handler und macht Aussagen zur Protokollausgabe: http://octodecillion.com/blog/jmockit-test-logging/quelle
Es gibt zwei Dinge, die Sie möglicherweise testen möchten.
Diese beiden Dinge sind tatsächlich verschiedene Dinge und könnten daher separat getestet werden. Das Testen des zweiten (des Textes von Nachrichten) ist jedoch so problematisch, dass ich davon abraten würde, es überhaupt zu tun. Ein Test eines Nachrichtentextes besteht letztendlich darin, zu überprüfen, ob eine Textzeichenfolge (der erwartete Nachrichtentext) mit der in Ihrem Protokollierungscode verwendeten Textzeichenfolge identisch ist oder trivial daraus abgeleitet werden kann.
Beachten Sie, dass Ihr Programmcode (der möglicherweise eine Geschäftslogik implementiert) direkt die Textprotokollierungsschnittstelle aufruft, ein schlechtes Design ist (aber leider sehr häufig). Code, der für die Geschäftslogik verantwortlich ist, entscheidet auch über einige Protokollierungsrichtlinien und den Text von Protokollnachrichten. Es mischt Geschäftslogik mit Benutzeroberflächencode (ja, Protokollmeldungen sind Teil der Benutzeroberfläche Ihres Programms). Diese Dinge sollten getrennt sein.
Ich empfehle daher, dass die Geschäftslogik den Text von Protokollnachrichten nicht direkt generiert. Lassen Sie es stattdessen an ein Protokollierungsobjekt delegieren.
implements
aninterface
, die die interne API beschreibt, die Ihre Geschäftslogik möglicherweise verwendet.interface
.Sie können dann testen, ob Ihre Geschäftslogikklassen die Protokollierungsschnittstelle korrekt über Ereignisse informieren, indem Sie einen Scheinprotokollierer erstellen, der die interne Protokollierungs-API implementiert, und in der Einrichtungsphase Ihres Tests die Abhängigkeitsinjektion verwenden.
So was:
quelle
Was ich getan habe, wenn ich nur sehen möchte, dass eine Zeichenfolge protokolliert wurde (im Gegensatz zur Überprüfung der genauen Protokollanweisungen, die einfach zu spröde sind), ist, StdOut in einen Puffer umzuleiten, ein Include auszuführen und dann StdOut zurückzusetzen:
quelle
java.util.logging
(obwohl ich es verwendet habeSystem.setErr(new PrintStream(buffer));
, weil es sich bei stderr anmeldet), aber es funktioniert nicht (der Puffer bleibt leer). Wenn ichSystem.err.println("foo")
direkt verwende, funktioniert es, daher gehe ich davon aus, dass das Protokollierungssystem seine eigene Referenz des Ausgabestreams behält, aus dem es stammtSystem.err
, sodass mein Aufruf vonSystem.setErr(..)
keine Auswirkung auf die Protokollausgabe hat, wie dies nach der Initialisierung des Protokollsystems geschieht.Ich habe eine ähnliche Frage für log4 beantwortet. Siehe, wie kann ich mit junit testen, dass eine Warnung mit log4 protokolliert wurde?
Dies ist neuer und Beispiel mit Log4j2 (getestet mit 2.11.2) und Junit 5;
Verwenden der folgenden Maven-Abhängigkeiten
quelle
Wenn Sie log4j2 verwenden, konnte ich mit der Lösung von https://www.dontpanicblog.co.uk/2018/04/29/test-log4j2-with-junit/ bestätigen , dass Nachrichten protokolliert wurden.
Die Lösung lautet wie folgt:
Definieren Sie einen log4j-Appender als ExternalResource-Regel
Definieren Sie einen Test, der Ihre ExternalResource-Regel verwendet
Vergessen Sie nicht, log4j2.xml als Teil von src / test / resources zu haben
quelle