35

通常会在代码中看到日志记录功能:

public class A {

    private static final Log LOG = LogFactory.getLog(A.class);

和用法:

} catch (Exception e) {
    LOG.error(e.getMessage(), e);
    throw e;
}

但我从来没有见过这样的代码的单个单元测试。

当然我会测试抛出异常和异常类型,但是我应该编写测试来检查日志信息吗?我倾向于认为日志记录是系统行为的另一部分,因此在测试中覆盖它是合乎逻辑的。

假设我应该涵盖它,意味着我应该更改我的原始代码以注入模拟日志并检查是否使用预期消息调用了“错误”方法。但是如果我的原始类是服务并且它是由spring实例化的,我应该注入一些记录器以及其他依赖项怎么办?

4

6 回答 6

18

测试日志库不取决于您。但是值得测试的是,当抛出异常时,您的类会在正确的级别记录一条消息。您正在测试的是您的代码使用日志库做正确的事情。

要使上面的代码可测试,请使用依赖注入。这假设记录器实现了一个接口,ILog. 您可以将记录器作为构造函数参数传递给类 A。然后测试代码将创建 的模拟实现ILog,并将其传递给构造函数。上面的代码中没有显示异常是如何发生的,但可能是通过其他一些依赖对象。所以你也嘲笑它,让它抛出一个异常。然后检查模拟是否ILog调用了该error方法。也许您想检查它记录的消息,但这可能太过分了,因为这会使测试代码变得脆弱。

于 2012-08-17T01:51:10.323 回答
7

是的,我们应该在日志记录执行所需的操作时测试日志记录。例如,您在某些外部应用程序中有钩子,用于扫描日志以查找某些事件。在这种情况下,您当然希望确保日志记录已完成。

当然,您不想测试每个日志事件,我认为大多数情况下应该只测试 ERROR(而不是全部)。

使用现代日志框架(例如 SLF4j),您可以简单地注入一个自定义处理程序,将事件存储在内存中,然后可以对其进行断言。

我现在想到的有两个:

SLF4JTesting:不需要修改日志配置,但需要注入可能导致修改代码的日志工厂。

SLF4J 测试:不如 slf4jtesting 强大,似乎还没有开发,但与现有代码配合得很好。除了用于测试的记录器配置外,没有任何修改。

使用 SLF4J 测试时,断言非常严格并检查整个事件是否相等。在这种情况下,自定义匹配器可能很有趣:

public static Matcher<LoggingEvent> errorMessageContains(final String s) {
    return new TypeSafeMatcher<LoggingEvent>() {
        @Override
        public void describeTo(final Description description) {
            description.appendText(" type " + Level.ERROR + " should contain ")
                    .appendValue(s);
        }

        @Override
        protected void describeMismatchSafely(final LoggingEvent item, final Description mismatchDescription) {
            mismatchDescription.appendText(" was type ").appendValue(l)
                    .appendText(" message ").appendValue(item.getMessage());
        }

        @Override
        protected boolean matchesSafely(final LoggingEvent item) {
            return item.getLevel().equals(Level.ERROR)
                    && item.getMessage().contains(s);
        }
    };
}

这仅检查消息是否包含文本,但不检查是否相等。因此,当修改消息以修复错字或提供更多细节时,如果仍然包含基本部分,则测试不会中断。

于 2017-04-24T09:07:31.523 回答
1

如果日志记录是一项业务需求,并且会提供业务价值(即在发生故障时诊断或分类问题),那么您应该将其视为任何其他需求。因此,您可能应该编写单元测试,而不是验证您的日志库是否工作,而是验证在预期的情况下,您的代码记录了它应该记录的内容。

有关此主题的更多信息:https ://ardalis.com/logging-and-monitoring-are-requirements

于 2017-08-10T12:32:35.447 回答
1

还有另一种方法:你可以模拟 LogFactory!例如:

import junit.framework.Assert;
import mockit.Mock;
import mockit.MockUp;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Test;

public class XXXTest {
    class MyLog implements Log {
        public static final String INFO = "info";

        private String logLevel;
        private Object logContent;

        public String getLogLevel() {
            return logLevel;
        }

        public Object getLogContent() {
            return logContent;
        }

        @Override
        public void info(Object o) {
            logLevel = "info";
            logContent = o;
        }

        //Implement other methods
    }

    @Test
    public void testXXXFunction() {
        final MyLog log = new MyLog();
        new MockUp<LogFactory>() {
            @Mock
            public Log getLog(String name) {
                return log;
            }
        };

        //invoke function and log by MyLog
        FunctionToBeTest.invoke();
        Assert.assertEquals("expected log text", log.getLogContent());
    }
}

祝你好运!

于 2017-09-21T07:39:00.663 回答
1

我肯定会考虑对日志场景进行单元测试。测试时,请考虑在代码失败的情况下您需要的信息。如果您遇到实际问题,您希望确信您有足够的信息来找到问题的原因。

避免过多的日志记录,因为在调试时您可能看不到树林中的木头。

于 2020-08-14T09:38:01.597 回答
0

我不会对除了调用您信任的库之外什么都不做的代码进行单元测试。
你信任你的日志库吗?如果测试失败,是因为库中存在错误,还是仅仅因为您没有正确配置库?你关心测试配置吗?

于 2012-08-17T01:36:50.137 回答