Подтвердить что ты не робот

Каков наилучший способ для модульного тестирования сообщений журнала SLF4J?

Я использую slf4j, и я хочу unit test мой код, чтобы убедиться, что сообщения об ошибках/сообщениях ошибок генерируются при определенных условиях. Я бы предпочел, чтобы это были строгие модульные тесты, поэтому я бы предпочел не затягивать конфигурацию протоколирования из файла, чтобы проверить, генерируются ли сообщения журнала. Издевательская структура, которую я использую, - Mockito.

4b9b3361

Ответ 1

Я думаю, вы могли бы решить свою проблему с помощью пользовательского приложения. Создайте тестовый appender, который реализует org.apache.log4j.Appender, и установите ваш appender в log4j.properties и загрузите его при выполнении тестовых примеров.

Если вы вернетесь к тестовой ленте из этого appender, вы можете проверить зарегистрированные сообщения

Ответ 2

Для тестирования slf4j, не полагаясь на конкретную реализацию (например, log4j), вы можете предоставить свою собственную реализацию регистрации slf4j, как описано в этот вопрос SLF4J. Ваша реализация может записывать сообщения, которые были занесены в журнал, а затем опросить ваши модульные тесты для проверки.

Пакет slf4j-test выполняет именно это. Это реализация регистрации slf4j в памяти, которая предоставляет методы для извлечения зарегистрированных сообщений.

Ответ 3

Лучшая тестовая реализация SLF4J, которая очень хорошо работает в среде с одновременным выполнением теста, https://github.com/portingle/slf4jtesting

Я включил несколько дискуссий по тестированию журналов slf4j и ограничения существующих тестовых подходов, когда дело доходит до выполнения одновременного тестирования.

Я решил поместить свои слова в код и что результат git repo - это результат.

Ответ 4

Вместо того, чтобы насмехаться над SLF4J, вы можете разместить важные вызовы журналов, необходимые для тестирования внутри своих собственных методов, которые вы можете легко высмеять.

Если вы действительно хотите издеваться над SLF4J, я бы поспорил, что вы можете создать для него своего провайдера, который позволит вам поставлять макетный регистратор со стороны SLF4J вместо того, чтобы вводить его в свои объекты обслуживания.

Ответ 5

Создайте тестовое правило:

import ch.qos.logback.classic.Logger;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.read.ListAppender;
import org.junit.rules.TestRule;
import org.junit.runner.Description;
import org.junit.runners.model.Statement;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.stream.Collectors;

public class LoggerRule implements TestRule {

  private final ListAppender<ILoggingEvent> listAppender = new ListAppender<>();
  private final Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);

  @Override
  public Statement apply(Statement base, Description description) {
    return new Statement() {
      @Override
      public void evaluate() throws Throwable {
        setup();
        base.evaluate();
        teardown();
      }
    };
  }

  private void setup() {
    logger.addAppender(listAppender);
    listAppender.start();
  }

  private void teardown() {
    listAppender.stop();
    listAppender.list.clear();
    logger.detachAppender(listAppender);
  }

  public List<String> getMessages() {
    return listAppender.list.stream().map(e -> e.getMessage()).collect(Collectors.toList());
  }

  public List<String> getFormattedMessages() {
    return listAppender.list.stream().map(e -> e.getFormattedMessage()).collect(Collectors.toList());
  }

}

Тогда используйте это:

@Rule
public final LoggerRule loggerRule = new LoggerRule();

@Test
public void yourTest() {
    // ...
    assertThat(loggerRule.getFormattedMessages().size()).isEqualTo(2);
}

Ответ 6

Подобно @Zsolt, вы можете mock log4j Appender и установить его на Logger, а затем проверить вызовы на Appender.doAppend(). Это позволяет вам протестировать без изменения реального кода.

Ответ 7

Так вот мой метод.

Во-первых, я позволяю логгеру вводиться. Но я также предоставляю значение по умолчанию:

package com.mycompany.myproject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyCoolClass { //implements IMyCoolClass {

    private static final String PROCESS_STARTED = "Process started. (key='%1$s')";

    private final Logger logger;

    public MyCoolClass() {
        this(LoggerFactory.getLogger(MyCoolClass.class));
    }

    public MyCoolClass(Logger lgr) {
        this.logger = lgr;
    }

    public doSomething(int key)
    {
        logger.info(String.format(PROCESS_STARTED, key));
        /*now go do something */
    }
}

Тогда я написал очень простой в памяти логгер

import org.slf4j.Marker;

import java.util.ArrayList;
import java.util.Collection;

public class InMemoryUnitTestLogger implements org.slf4j.Logger {

    public Collection<String> informations = new ArrayList<String>();
    public Collection<String> errors = new ArrayList<String>();
    public Collection<String> traces = new ArrayList<String>();
    public Collection<String> debugs = new ArrayList<>();
    public Collection<String> warns = new ArrayList<>();

    public Collection<String> getInformations() {
        return informations;
    }

    public Collection<String> getErrors() {
        return errors;
    }

    public Collection<String> getTraces() {
        return traces;
    }

    public Collection<String> getDebugs() {
        return debugs;
    }

    public Collection<String> getWarns() {
        return warns;
    }



    @Override
    public String getName() {
        return "FakeLoggerName";
    }

    @Override
    public boolean isTraceEnabled() {
        return false;
    }

    @Override
    public boolean isTraceEnabled(Marker marker) {
        return false;
    }

    @Override
    public boolean isDebugEnabled() {
        return false;
    }

    @Override
    public boolean isDebugEnabled(Marker marker) {
        return false;
    }

    @Override
    public boolean isWarnEnabled(Marker marker) {
        return false;
    }

    @Override
    public boolean isInfoEnabled(Marker marker) {
        return false;
    }

    @Override
    public boolean isWarnEnabled() {
        return false;
    }

    @Override
    public boolean isErrorEnabled(Marker marker) {
        return false;
    }

    @Override
    public boolean isInfoEnabled() {
        return false;
    }

    @Override
    public boolean isErrorEnabled() {
        return false;
    }

    @Override
    public void trace(String s) {
        this.internalTrace(s);
    }

    @Override
    public void trace(String s, Object o) {
        this.internalTrace(s);
    }

    @Override
    public void trace(String s, Object o, Object o1) {
        this.internalTrace(s);
    }

    @Override
    public void trace(String s, Object... objects) {
        this.internalTrace(s);
    }

    @Override
    public void trace(String s, Throwable throwable) {
        this.internalTrace(s);
    }


    @Override
    public void trace(Marker marker, String s) {
        this.internalTrace(s);
    }

    @Override
    public void trace(Marker marker, String s, Object o) {
        this.internalTrace(s);
    }

    @Override
    public void trace(Marker marker, String s, Object o, Object o1) {
        this.internalTrace(s);
    }

    @Override
    public void trace(Marker marker, String s, Object... objects) {
        this.internalTrace(s);
    }

    @Override
    public void trace(Marker marker, String s, Throwable throwable) {
        this.internalTrace(s);
    }

    @Override
    public void debug(String s) {
        this.internalDebug(s);
    }

    @Override
    public void debug(String s, Object o) {
        this.internalDebug(s);
    }

    @Override
    public void debug(String s, Object o, Object o1) {
        this.internalDebug(s);
    }

    @Override
    public void debug(String s, Object... objects) {
        this.internalDebug(s);
    }

    @Override
    public void debug(String s, Throwable throwable) {
        this.internalDebug(s);
    }

    @Override
    public void debug(Marker marker, String s) {
        this.internalDebug(s);
    }

    @Override
    public void debug(Marker marker, String s, Object o) {
        this.internalDebug(s);
    }

    @Override
    public void debug(Marker marker, String s, Object o, Object o1) {
        this.internalDebug(s);
    }

    @Override
    public void debug(Marker marker, String s, Object... objects) {
        this.internalDebug(s);
    }

    @Override
    public void debug(Marker marker, String s, Throwable throwable) {
        this.internalDebug(s);
    }

    public void info(String s) {
        this.internalInfo(s);
    }

    @Override
    public void info(String s, Object o) {
        this.internalInfo(s);
    }

    @Override
    public void info(String s, Object o, Object o1) {
        this.internalInfo(s);
    }

    @Override
    public void info(String s, Object... objects) {
        this.internalInfo(s);
    }

    @Override
    public void info(String s, Throwable throwable) {
        this.internalInfo(s);
    }

    @Override
    public void info(Marker marker, String s) {
        this.internalInfo(s);
    }

    @Override
    public void info(Marker marker, String s, Object o) {
        this.internalInfo(s);
    }

    @Override
    public void info(Marker marker, String s, Object o, Object o1) {
        this.internalInfo(s);
    }

    @Override
    public void info(Marker marker, String s, Object... objects) {
        this.internalInfo(s);
    }

    @Override
    public void info(Marker marker, String s, Throwable throwable) {
        this.internalInfo(s);
    }

    public void error(String s) {
        this.internalError(s);
    }

    @Override
    public void error(String s, Object o) {
        this.internalError(s);
    }

    @Override
    public void error(String s, Object o, Object o1) {
        this.internalError(s);
    }

    @Override
    public void error(String s, Object... objects) {
        this.internalError(s);
    }

    @Override
    public void error(String s, Throwable throwable) {
        this.internalError(s);
    }

    @Override
    public void error(Marker marker, String s) {
        this.internalError(s);
    }

    @Override
    public void error(Marker marker, String s, Object o) {
        this.internalError(s);
    }

    @Override
    public void error(Marker marker, String s, Object o, Object o1) {
        this.internalError(s);
    }

    @Override
    public void error(Marker marker, String s, Object... objects) {
        this.internalError(s);
    }

    @Override
    public void error(Marker marker, String s, Throwable throwable) {
        this.internalError(s);
    }

    public void warn(String s) {
        this.internalWarn(s);
    }

    @Override
    public void warn(String s, Object o) {
        this.internalWarn(s);
    }

    @Override
    public void warn(String s, Object... objects) {
        this.internalWarn(s);
    }

    @Override
    public void warn(String s, Object o, Object o1) {
        this.internalWarn(s);
    }

    @Override
    public void warn(String s, Throwable throwable) {
        this.internalWarn(s);
    }

    @Override
    public void warn(Marker marker, String s) {
        this.internalWarn(s);
    }

    @Override
    public void warn(Marker marker, String s, Object o) {
        this.internalWarn(s);
    }

    @Override
    public void warn(Marker marker, String s, Object o, Object o1) {
        this.internalWarn(s);
    }

    @Override
    public void warn(Marker marker, String s, Object... objects) {
        this.internalWarn(s);
    }

    @Override
    public void warn(Marker marker, String s, Throwable throwable) {
        this.internalWarn(s);
    }

    private void internalDebug(String s) {
        System.out.println(s);
        this.debugs.add(s);
    }

    private void internalInfo(String msg) {
        System.out.println(msg);
        this.informations.add(msg);
    }

    private void internalTrace(String msg) {
        //??System.out.println(msg);
        this.traces.add(msg);
    }


    private void internalWarn(String msg) {
        System.err.println(msg);
        this.warns.add(msg);
    }

    private void internalError(String msg) {
        System.err.println(msg);
        this.errors.add(msg);
    }

Затем в своих модульных тестах я могу сделать одну из двух вещей:

private ByteArrayOutputStream setupSimpleLog(Logger lgr) {
    ByteArrayOutputStream pipeOut = new ByteArrayOutputStream();
    PrintStream pipeIn = new PrintStream(pipeOut);
    System.setErr(pipeIn);
    return pipeOut;
}

private Logger getSimpleLog() {
    Logger lgr = new InMemoryUnitTestLogger();
    return lgr;
}


private void myTest()
{


    Logger lgr = getSimpleLog();
    ByteArrayOutputStream pipeOut = this.setupSimpleLog(lgr);

    MyCoolClass testClass = new MyCoolClass(lgr);
    int myValue = 333;
    testClass.doSomething(myValue);

    String findMessage = String.format(MyCoolClass.PROCESS_STARTED, myValue);
    String output = new String(pipeOut.toByteArray());
    assertTrue(output.contains(findMessage));
}

или аналогично приведенному выше, но сделайте каст на пользовательский логгер

private void myTest()
{


    Logger lgr = getSimpleLog();
    MyCoolClass testClass = new MyCoolClass(lgr);
    int myValue = 333;
    testClass.doSomething(myValue);

    String findMessage = String.format(MyCoolClass.PROCESS_STARTED, myValue);
    InMemoryUnitTestLogger castLogger = (InMemoryUnitTestLogger)lgr;
    /* now check the exact subcollection for the message) */
    assertTrue(castLogger.getInfos().contains(findMessage));
}

Взять код с крупинкой соли, идеи есть. Я не скомпилировал код.