Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/apache/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 我们应该进行单元测试日志记录吗?_Java_Unit Testing_Logging - Fatal编程技术网

Java 我们应该进行单元测试日志记录吗?

Java 我们应该进行单元测试日志记录吗?,java,unit-testing,logging,Java,Unit Testing,Logging,在代码中通常可以看到日志记录功能: public class A { private static final Log LOG = LogFactory.getLog(A.class); 使用方法: } catch (Exception e) { LOG.error(e.getMessage(), e); throw e; } 但我从未见过这种代码的单单元测试 当然我会测试抛出异常和异常类型,但我应该编写测试来检查日志信息吗?我倾向于认为日志记录是系统行为的另一部分

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

public class A {

    private static final Log LOG = LogFactory.getLog(A.class);
使用方法:

} catch (Exception e) {
    LOG.error(e.getMessage(), e);
    throw e;
}
但我从未见过这种代码的单单元测试

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


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

我不会对只调用您信任的库的代码进行单元测试。

你信任你的日志库吗?如果测试失败,是因为库中存在错误,还是仅仅因为您没有正确配置库?您关心测试配置吗?

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


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

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

当然,您不想测试每个登录事件,我认为应该测试的主要是错误(而不是所有错误)

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

我现在想到了其中两个:

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

:没有SLF4J测试强大,似乎没有开发出来,但与现有代码配合良好。除用于测试的记录器配置外,未进行任何修改

使用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);
        }
    };
}
公共静态匹配器errorMessageContains(最终字符串s){
返回新的TypeSafeMatcher(){
@凌驾
公共无效说明(最终说明){
description.appendText(“类型”+Level.ERROR+“应包含”)
.附加值;
}
@凌驾
受保护的无效描述不匹配安全(最终记录事件项,最终描述不匹配描述){
描述不匹配。appendText(“was类型”)。appendValue(l)
.appendText(“消息”).appendValue(item.getMessage());
}
@凌驾
受保护的布尔匹配安全(最终日志事件项){
返回项.getLevel().equals(Level.ERROR)
&&item.getMessage()包含个项目;
}
};
}

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

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


有关此主题的更多信息:

还有另一种方法:您可以模拟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());
    }
}
import junit.framework.Assert;
导入mockit.Mock;
导入mockit.MockUp;
导入org.apache.commons.logging.Log;
导入org.apache.commons.logging.LogFactory;
导入org.junit.Test;
公共类XXXTest{
类MyLog实现日志{
公共静态最终字符串INFO=“INFO”;
私有字符串日志级别;
私有对象日志内容;
公共字符串getLogLevel(){
返回对数电平;
}
公共对象getLogContent(){
返回日志内容;
}
@凌驾
公共无效信息(对象o){
logLevel=“info”;
logContent=o;
}
//实施其他方法
}
@试验
public void testXXXFunction(){
最终MyLog日志=新的MyLog();
新模型(){
@嘲弄
公共日志getLog(字符串名称){
返回日志;
}
};
//通过MyLog调用函数和日志
FunctionToBeTest.invoke();
Assert.assertEquals(“预期的日志文本”,log.getLogContent());
}
}

祝你好运

>我肯定会考虑日志测试的单元测试。测试时,考虑在代码失败的情况下需要的信息。如果您有一个实时问题,您希望得到保证,您有足够的信息来找到问题的原因

避免过度记录,因为您可能看不到wo