java 我们应该单元测试日志记录吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11998713/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 07:13:02  来源:igfitidea点击:

Should we unit test logging?

javaunit-testinglogging

提问by Rrr

It's usual to see logging functionality in the code:

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

public class A {

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

and usage:

和用法:

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

but I never saw even single unit test for such code.

但我什至从未见过此类代码的单个单元测试。

Off course I do test throwing exception and exception type, but should I write test for checking logging information? I tend to think that logging is another part of system behavior, so it's quit logically to cover it in the tests.

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

Assuming that I should cover it, means that I should change my original code to inject mock log and check that "error" method was invoked with expected message. But what to do if my original class is service and it's instantiated by spring, should I inject some logger as well as other dependencies?

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

采纳答案by Carl Raymond

It's not up to you to test the logging library. But it can be worthwhile to test that when an exception is thrown, your class logs a message at the right level. What you're testing is that your code does the right thing with the logging library.

测试日志库不是由您决定的。但是值得测试一下,当抛出异常时,您的类在正确的级别记录一条消息。您正在测试的是您的代码使用日志库执行正确的操作。

To make the code above testable, use dependency injection. This assumes that the logger implements an interface, ILog. You would pass in the logger as a constructor parameter to class A. Then the test code would create a mock implementation of ILog, and pass that into the constructor. Not shown in the code above is how the exception comes about, but presumably it would be through some other dependent object. So you mock that as well, and make it throw an exception. Then check that the mock ILoginvoked the errormethod. Maybe you want to examine the message that it logs, but that might be going too far, by making the test code fragile.

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

回答by kap

Yes, we should test logging when the logging is doing something that is required. For example, you have hooks in some external application that scans the log for certain events. In that case you certainly want to ensure the logging is done.

是的,我们应该在日志记录做一些需要的事情时测试日志记录。例如,您在某些外部应用程序中有挂钩,可以扫描某些事件的日志。在这种情况下,您当然希望确保完成日志记录。

Of course you do not want to test every loging event, and I would think that mostly only ERROR (and not all of them) should be tested.

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

With modern logging frameworks such as SLF4j you can simply inject a custom handler that stores the events for in memory and that can be asserted against afterwards.

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

There are two of them that come to my mind right now:

我现在想到的有两个:

SLF4JTesting: Requires no modification of logging configuration but requires to inject a logging factory which might lead to modified code.

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

SLF4J Test: Not as powerful as slf4jtesting and seems not to be developed, but works well with existing code. No modifications besides the logger configuration for test.

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

When using SLF4J Test, the assertions are quite strict and check the whole event for equality. A custom matcher is probably interesting in such a case:

使用 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);
        }
    };
}

This only checks that the message contains a text but not if it is equal. Thus, when the message is modified to fix a typo or give more detail, the test does not break if the essential part is still contained.

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

回答by Ming Cheng

there's another way: you can mock LogFactory! for example:

还有另一种方法:您可以模拟 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());
    }
}

good luck!

祝你好运!

回答by ssmith

If the logging is a business requirement, and will provide business value (i.e. in the event of a failure, to diagnose or triage a problem), then you should treat it as any other requirement. As such, you should probably write unit tests not to verify that your logging library works, but to verify that, under the expected circumstances, your code logs what it should.

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

More on this topic: https://ardalis.com/logging-and-monitoring-are-requirements

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

回答by Matt Ball

I wouldn't unit test code that does nothing but call into a library that you trust.
Do you trust your logging library? If the test fails, is it because there's a bug in the library, or simply because you haven't configured the library correctly? Do you careabout testing the configuration?

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