Jak testować logger ?

Jak testować logger ?

Po co w ogóle testować logowanie?

Logi to jedno z głównych źródeł informacji co dzieje się z naszą aplikacją. Bardzo często zawierają informacje krytyczne aby zdiagnozować przyczynę problemu na produkcji. Mogą również służyć do monitorowania zarówno stanu naszej aplikacji i przebiegu procesów biznesowy.

Łatwo jednak w trakcie refactor’ingu „zgubić” jakiś log, co może skutkować „cichym” wyłączeniem monitoringu lub brakiem możliwości odpowiedzi na odwieczne pytanie „Dlaczego?” podczas błędów na produkcji.

Najlepszym sposobem by przed taką sytuacja się uchronić jest moim zdaniem napisanie testów, najlepiej szybkich.

Przykładowy serwis

Potrzebujemy zatem przykładowy serwis, który liczy coś bardzo ważnego, a wynik jest zarówno zwrócony, jak i logowany. Sam log w celu odróżnienia od setek innych podobnych zawiera dodatkową informacją dodana przez MDC.

oraz test:

W zależności od użytej implementacji przez nas Logger’a będziemy musili w trochę inny osób napisać fake / in memory implementację.

Logback + Slf4j

Jeżeli używamy Logback’a wówczas musimy stworzyć własną implementację „InMemory”, która rozszerzy klasę ListAppender<ILoggingEvent>. Nie musimy przeciążać żadnej metody. Klasa ListAppender daje nam dostęp do listy (super.list) wszystkich zdarzeń, które zostały zalogowane. Po naszej stronie zostaje napisanie metod które pozwolą nam sprawdzić czy oczekiwane przez nas zdarzenie znajduje się na liście. Przykładowa implementacja mogła by wyglądać tak:

Teraz pozostaje nam już tylko użyć naszej implementacji w teście poprzez dodanie naszego InMemoryLogger’a jako appendera:

Przykład logback na Github

Log4j2 + Slf4j

Jeżeli korzystasz z Log4j2 najszybciej i najłatwiej zrobić własną implementację Appender’a rozszerzającą AbstractAppender. Wystarczy wówczas nadpisać tylko metodę void append, która odpowiada za zalogowanie EvntLogu. W naszej implementacji wystarczy, że przechowamy takie zdarzenie w kolekcji w celu późniejszej weryfikacji.

W testowej implementacji śmiało można pominąć aspekt konfiguracji poprzez np. użycie w konstruktorze stałych / wartości domyślny. Możemy również zignorować adnotacje Log4j2 (@Plugin), które pozwalają w realnych warunkach użyć customowego appendera jako pluginu.

Żeby log4j2 użył naszej implementacji musimy mu ją jawnie wskazać w teście:

Przykład log4j na Github

Voilà! Prawda że proste ?