Мне это тоже нужно было несколько раз. Ниже я собрал небольшой образец, который вы бы хотели адаптировать к вашим потребностям. По сути, вы создаете свой собственный Appender
и добавляете его в нужный вам регистратор. Если вы хотите собрать все, корневой журнал - хорошее место для начала, но вы можете использовать более конкретный, если хотите. Не забудьте удалить Appender, когда вы закончите, иначе вы можете создать утечку памяти. Ниже я сделал это в рамках теста, но setUp
или @Before
и, tearDown
или, @After
может быть, лучше, в зависимости от ваших потребностей.
Кроме того, реализация ниже собирает все List
в памяти. Если вы ведете много журналов, вы можете рассмотреть возможность добавления фильтра для удаления скучных записей или записи журнала во временный файл на диске (подсказка: LoggingEvent
есть Serializable
, так что вы можете просто сериализовать объекты событий, если ваше сообщение журнала является.)
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()
, затем пройти и позвонитьappender.setThreshold(Level.OFF)
каждому (и сбросить их, когда вы закончите!). Это гарантирует, что «плохие» сообщения, которые вы пытаетесь сгенерировать, не появятся в журналах тестов и не пугает следующего разработчика.ListAppender<ILoggingEvent>
вместо создания своего собственного приложения.Logger
кorg.apache.logging.log4j.core.Logger
(класс реализации для интерфейса), вы получите доступsetAppender()/removeAppender()
снова.Вот простое и эффективное решение Logback.
Не требует добавления / создания нового класса.
Он опирается на
ListAppender
: приложение для входа в систему «белого ящика», где записи журнала добавляются вpublic List
поле, которое мы могли бы использовать для подтверждения.Вот простой пример.
Класс фу:
Класс FooTest:
Утверждения JUnit не очень приспособлены для утверждения некоторых специфических свойств элементов списка.
Библиотеки соответствия / утверждения как AssertJ или Hamcrest выглядят лучше для этого:
С AssertJ это будет:
источник
mock
класс, который тестируется. Вам нужно создать экземпляр сnew
операторомБольшое спасибо за эти (удивительно) быстрые и полезные ответы; они поставили меня на правильный путь для моего решения.
Кодовая база, в которой я хочу использовать это, использует java.util.logging в качестве механизма ведения журнала, и я не чувствую себя как дома в этих кодах, чтобы полностью изменить это на log4j или на интерфейсы / фасады регистратора. Но, основываясь на этих предложениях, я «взломал» расширение julhandler, и это работает как удовольствие.
Краткое резюме следует. Расширить
java.util.logging.Handler
:Очевидно, вы можете хранить столько, сколько хотите / хотите / нужно от
LogRecord
, или помещать их все в стек, пока не получите переполнение.При подготовке к тесту junit вы создаете
java.util.logging.Logger
и добавляетеLogHandler
в него следующее:Требование
setUseParentHandlers()
состоит в том, чтобы заставить замолчать обычные обработчики, чтобы (для этого запуска теста junit) не происходило ненужного ведения журнала. Сделайте все, что нужно вашему тестируемому коду, чтобы использовать этот регистратор, запустите тест и assertEquality:(Конечно, вы бы переместили большую часть этой работы в
@Before
метод и внесли множество других улучшений, но это загромождает эту презентацию.)источник
Другой вариант - смоделировать Appender и проверить, было ли сообщение зарегистрировано для этого Appender. Пример для Log4j 1.2.x и mockito:
источник
По сути, вы тестируете побочный эффект зависимого класса. Для модульного тестирования вам нужно только убедиться, что
был вызван с правильным параметром. Следовательно, используйте эмулирующую среду для эмуляции логгера, и это позволит вам проверить поведение вашего класса.
источник
Пересмешка - вариант здесь, хотя это было бы трудно, потому что регистраторы, как правило, являются частными static static final - так что установка фиктивного регистратора не будет легкой задачей или потребует модификации тестируемого класса.
Вы можете создать пользовательский Appender (или как он там называется) и зарегистрировать его - либо через файл конфигурации только для тестирования, либо во время выполнения (в зависимости от структуры ведения журнала). И затем вы можете получить этот appender (либо статически, если он объявлен в файле конфигурации, либо по его текущей ссылке, если вы подключаете его во время выполнения), и проверить его содержимое.
источник
Вдохновленный решением @ RonaldBlaschke, я придумал это:
... который позволяет вам делать:
Вероятно, вы могли бы сделать так, чтобы он использовал хамкрест более умным способом, но я оставил это на этом.
источник
Для log4j2 решение немного отличается, потому что AppenderSkeleton больше не доступен. Кроме того, использование Mockito или подобной библиотеки для создания Appender с ArgumentCaptor не будет работать, если вы ожидаете нескольких сообщений регистрации, поскольку MutableLogEvent повторно используется в нескольких сообщениях журнала. Лучшее решение, которое я нашел для log4j2:
источник
Как уже упоминалось от других, вы можете использовать насмешливую структуру. Чтобы это работало, вы должны выставить регистратор в своем классе (хотя я бы предпочел сделать его закрытым, а не создавать открытый сеттер).
Другое решение - создать фальшивый регистратор вручную. Вы должны написать фальшивый регистратор (больше кода фикстуры), но в этом случае я бы предпочел улучшенную читаемость тестов по сравнению с сохраненным кодом из фреймворка.
Я бы сделал что-то вроде этого:
источник
Вот это да. Я не уверен, почему это было так сложно. Я обнаружил, что не смог использовать ни один из приведенных выше примеров кода, потому что я использовал log4j2 вместо slf4j. Это мое решение:
источник
Вот что я сделал для входа.
Я создал класс TestAppender:
Затем в родительском классе тестового модуля я создал метод:
У меня есть файл logback-test.xml, определенный в src / test / resources, и я добавил тестового приложения:
и добавил этот appender к корневому appender:
Теперь в моих тестовых классах, которые выходят из моего родительского тестового класса, я могу получить appender и зарегистрировать последнее сообщение, а также проверить сообщение, уровень, throwable.
источник
Для Junit 5 (Jupiter) SpringCaptureExtension весьма полезен. Он доступен начиная с Spring Boot 2.2 и доступен в тесте весенней загрузки артефакте .
Пример (взят из Javadoc):
источник
getOut()
илиgetErr()
.Что касается меня , вы можете упростить тестирование с помощью
JUnit
сMockito
. Я предлагаю следующее решение для этого:Вот почему у нас есть хорошая гибкость для тестов с различным количеством сообщений
источник
when(appender.isStarted()).thenReturn(true); when(appender.getName()).thenReturn("Test Appender");
приведите регистратор на «org.apache.logging.log4j.core.Logger», добавьте и измените LoggingEvent -> LogEventисточник
API для Log4J2 немного отличается. Также вы можете использовать его async appender. Я создал защелку для этого:
Используйте это так:
источник
Обратите внимание , что в Log4J 2.x, открытый интерфейс
org.apache.logging.log4j.Logger
не включаетsetAppender()
иremoveAppender()
методы.Но если вы не делаете ничего слишком сложного, вы должны иметь возможность привести его к классу реализации
org.apache.logging.log4j.core.Logger
, который предоставляет эти методы.Вот пример с Mockito и AssertJ :
источник
Еще одна идея, о которой стоит упомянуть, хотя это и более старая тема, - это создание CDI-производителя для внедрения вашего логгера, чтобы сделать насмешку легкой. (И это также дает преимущество в том, что больше не нужно объявлять «весь оператор логгера», но это не по теме)
Пример:
Создание регистратора для внедрения:
Классификатор:
Используя регистратор в вашем производственном коде:
Тестирование регистратора в вашем тестовом коде (приведем пример easyMock):
источник
Используя Jmockit (1.21), я смог написать этот простой тест. Тест проверяет, что конкретное сообщение об ошибке вызывается только один раз.
источник
Насмешка над Appender может помочь захватить строки журнала. Найдите образец на: http://clearqa.blogspot.co.uk/2016/12/test-log-lines.html
источник
Используйте код ниже. Я использую тот же код для моего весеннего интеграционного теста, где я использую log back для регистрации. Используйте метод assertJobIsScheduled чтобы утверждать текст, напечатанный в журнале.
источник
Если вы используете
java.util.logging.Logger
эту статью, это может быть очень полезно, она создает новый обработчик и делает утверждения в журнале. Вывод: http://octodecillion.com/blog/jmockit-test-logging/источник
Есть две вещи, которые вы, возможно, пытаетесь проверить.
Эти две вещи на самом деле разные вещи, и поэтому могут быть проверены отдельно. Тем не менее, тестирование второго (текст сообщения) настолько проблематично, что я рекомендую вообще не делать этого. В конечном итоге проверка текста сообщения будет состоять из проверки того, что одна текстовая строка (ожидаемый текст сообщения) совпадает или может быть тривиально получена из текстовой строки, используемой в коде регистрации.
Обратите внимание, что наличие в программном коде (возможно, реализующего некоторую бизнес-логику) прямого вызова интерфейса ведения текстового журнала - плохой дизайн (но, к сожалению, очень общий). Код, отвечающий за бизнес-логику, также определяет политику ведения журнала и текст сообщений журнала. Он смешивает бизнес-логику с кодом пользовательского интерфейса (да, сообщения журнала являются частью пользовательского интерфейса вашей программы). Эти вещи должны быть отдельными.
Поэтому я рекомендую, чтобы бизнес-логика напрямую не генерировала текст сообщений журнала. Вместо этого пусть он делегируется объекту регистрации.
implements
interface
interface
.Затем вы можете проверить, правильно ли ваши классы бизнес-логики сообщают интерфейсу ведения журнала о событиях, создав фиктивный регистратор, который реализует внутренний API ведения журнала, и используя внедрение зависимостей на этапе настройки вашего теста.
Как это:
источник
То, что я сделал, если все, что я хочу сделать, это увидеть, что какая-то строка была зарегистрирована (в отличие от проверки точных операторов журнала, которая слишком хрупкая), это перенаправить StdOut в буфер, создать содержимое, затем сбросить StdOut:
источник
java.util.logging
(хотя я использовалSystem.setErr(new PrintStream(buffer));
, потому что он регистрирует в stderr), но это не работает (буфер остается пустым). если я используюSystem.err.println("foo")
напрямую, это работает, поэтому я предполагаю, что система ведения журналов сохраняет собственную ссылку на выходной поток, из которого она получаетSystem.err
, поэтому мой вызовSystem.setErr(..)
не влияет на вывод журнала, как это происходит после инициализации системы журналирования.Я ответил на аналогичный вопрос для log4j: смотрите, как я могу протестировать с сообщением о том, что предупреждение было зарегистрировано с log4
Это новее и пример с Log4j2 (протестирован с 2.11.2) и junit 5;
Используя следующие maven-зависимости
источник
Если вы используете log4j2, решение из https://www.dontpanicblog.co.uk/2018/04/29/test-log4j2-with-junit/ позволило мне утверждать, что сообщения были зарегистрированы.
Решение выглядит так:
Определите приложение log4j как правило ExternalResource.
Определите тест, который использует ваше правило ExternalResource
Не забудьте иметь log4j2.xml в составе src / test / resources
источник