#java #junit #log4j
#java #junit #log4j
Вопрос:
У меня есть проект Java, который тестируется с JUnit (сочетание стилей Junit 3 и 4), где тестируемые классы могут регистрировать ошибку log4j. Я хотел бы, чтобы модульный тест завершался неудачей, если такая ошибка регистрируется.
Существует ли общий способ настроить log4j или инфраструктуру модульного тестирования так, чтобы любой вызов метода log4j error() в тестируемом коде вызывал исключение во время выполнения и, следовательно, завершал тест неудачно? AOP может быть одним из способов, но меня интересуют и другие возможности.
Цель здесь состоит в том, чтобы отсеять места в коде, где log4j error() используется неправильно. То есть, когда ошибка регистрируется, но никаких исключений или обработки ошибок не произошло, либо это на самом деле не ошибка, либо она есть и должна быть вызвана.
например:
public class MyTest extends TestCase {
public void testLogAnError() {
// Want to make this fail
new MyClass().logAnError();
}
}
public class MyClass() {
static Logger logger = Logger.getLogger("foo");
public void logAnError() {
// I'm logging an error, but not doing anything about it
logger.error("Something bad, or is it?");
// TODO throw an exception or don't log an error if there isn't one
}
}
Обновление: вот как выглядит решение, которое я использую в настоящее время. Он основан на ответе sbridges (добавлен в тестовый класс):
private static final Appender crashAndBurnAppender = new NullAppender () {
public void doAppend(LoggingEvent event) {
if(event.getLevel() == Level.ERROR) {
throw new AssertionError("logged at error:" event.getMessage());
}
}
};
Затем в программе установки:
Logger.getRootLogger().addAppender(crashAndBurnAppender);
И демонтаж:
Logger.getRootLogger().removeAppender(crashAndBurnAppender);
Ответ №1:
Вы можете создать новое приложение, которое выдает AssertionError при входе в систему на уровне ошибки.
Что-то вроде,
class TestAppender extends AppenderSkeleton {
public void doAppend(LoggingEvent event) {
if(event.getLevel() == Level.Error) {
throw new AssertionError("logged at error:" event.getMessage());
}
}
}
В вашем тесте выполните,
Logger.getRootLogger().addAppender(new TestAppender());
Редактировать: как указал Ральф, удалите TestAppender после завершения теста.
Комментарии:
1. Вам нужно отменить
Logger.getRootLogger().addAppender(new TestAppender());
после теста! Поскольку регистратор статичен, это добавляющее устройство также будет присутствовать в следующем тесте, если оно не будет удалено! — Вы должны сделать это с помощью@After
метода или оператора finally, потому что «обычный» код в вашем тесте не будет выполнен после исключения.
Ответ №2:
Решение Log4j2
Я только что наткнулся на это, но поскольку я использую версию Log4j 2, мне пришлось адаптировать код. Это заняло у меня довольно много времени, поскольку большинство ресурсов посвящено запутанному беспорядку конфигурационных фабрик, конфигураторов, контекстов регистратора и всего остального. То, что я сделал, может быть, не самым чистым или официальным способом, но это просто.
Определение такого служебного класса с двумя общедоступными статическими методами:
private static Appender appender;
private static void enableThrowOnLog(Level level) {
// Downcast: log4j.Logger -> log4j.core.Logger
Logger rootLogger = (Logger) LogManager.getRootLogger();
appender = new ThrowingAppender(level);
appender.start();
rootLogger.addAppender(appender);
}
public static void disableThrowOnLog() {
// Downcast: log4j.Logger -> log4j.core.Logger
Logger rootLogger = (Logger) LogManager.getRootLogger();
appender.stop();
rootLogger.removeAppender(appender);
appender = null;
}
private static class ThrowingAppender extends AbstractAppender {
private final Level level;
public ThrowingAppender(Level level) {
// Last parameter: ignoreExceptions -- must be false or exceptions will be swallowed
super("ThrowingAppender", new AbstractFilter() {}, PatternLayout.createDefaultLayout(), false);
this.level = level;
}
@Override
public synchronized void append(LogEvent event) {
Level eventLevel = event.getLevel();
if (eventLevel.isMoreSpecificThan(this.level)) {
String message = event.getMessage().getFormattedMessage();
throw new AppenderLoggingException(eventLevel " level was logged:n> " message);
}
}
}
В идеале, вы вызываете enableThrowOnLog()
внутри метода JUnit 4 @BeforeClass
и disableThrowOnLog()
внутри @AfterClass
метода, чтобы изменения в глобальной системе регистрации были должным образом отменены.