在单元测试中是否有任何简单的slf4j用法模式?

时间:2010-09-29 09:26:20

标签: java slf4j

我在我的项目中使用JUnit4和Hibernate3。 Hibernate依赖于Slf4j,因此我的项目也包含了这个库。现在我想在单元测试中使用Slf4j来记录补充测试信息。您能否提供一个简短的示例,说明我的单元测试应该如何只记录一行文本?在多次测试中,最好没有代码重复

9 个答案:

答案 0 :(得分:12)

为什么要在单元测试中记录内容?单元测试应该通过/失败,并应使用测试框架来表明。您不希望通过输出读取以查看测试是通过还是失败。如果它失败了,在IDE /调试器中运行它是解决它的最佳方法。

答案 1 :(得分:11)

我也喜欢在我的DAO类的JUnit测试中使用slf4j。当您创建新测试或修改旧测试时,它会有所帮助。我通常将我的旧日志输出保留在调试级别,并在信息级别创建新的日志记录语句,而我仍在积极处理该方法中的代码。我的一个JUnit类看起来像这样:

package com.example.mydao;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// other imports not shown...

public class TestMyDAO extends TestCase {

    private static final Logger logger = 
        LoggerFactory.getLogger(TestMyDAO.class);


    public void testA() {
        logger.debug("Logging from testA() method");
    }

    public void testB() {
        logger.debug("Logging from testB() method");
    }

    public void testThatIAmWorkingOn() {
        logger.info("Logging from my new test method at INFO level");
    }

}

我正在使用log4j作为实际的日志记录提供程序,因此我的log4j.xml配置文件如下所示:

<?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">
    <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="false">
    <appender name="consoleAppender" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p  [%c{1}] %m %n" />
        </layout>
    </appender>

    <logger name="com.example.mydao" additivity="false">
        <level value="INFO" />
        <appender-ref ref="consoleAppender"/>
    </logger>

    <logger name="org.hibernate" additivity="false">
        <level value="WARN" />
        <appender-ref ref="consoleAppender"/>
    </logger>

    <logger name="org.hibernate.connection.DriverManagerConnectionProvider" additivity="false">
        <level value="INFO" />
        <appender-ref ref="consoleAppender"/>
    </logger>

    <logger name="org.hibernate.connection.C3P0ConnectionProvider" additivity="false">
        <level value="INFO" />
        <appender-ref ref="consoleAppender"/>
    </logger>

    <logger name="com.mchange" additivity="false">
        <level value="WARN" />
        <appender-ref ref="consoleAppender"/>
    </logger>

    <logger name="com.mchange.v2.resourcepool.BasicResourcePool" additivity="false">
        <level value="INFO" />
        <appender-ref ref="consoleAppender"/>
    </logger>

    <logger name="com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource" additivity="false">
        <level value="INFO" />
        <appender-ref ref="consoleAppender"/>
    </logger>

    <logger name="com.mchange.v2.c3p0.C3P0Registry" additivity="false">
        <level value="INFO" />
        <appender-ref ref="consoleAppender"/>
    </logger>

    <root>
        <priority value ="WARN" />
        <appender-ref ref="consoleAppender"/>
    </root>

</log4j:configuration>

这给了我来自JUnit类的信息级输出,以及来自Hibernate运行时和Hibernate使用的其他库的一些有用的东西。根据自己的喜好调整。

最后,我需要确保在执行JUnit测试时,所有以下库都在我的类路径中:

  • slf4j-api-1.6.0.jar
  • slf4j-log4j12-1.6.0.jar
  • log4j-1.2.16.jar
  • log4j.xml(我的配置文件,如上所示)
  • 某些版本的JUnit运行时JAR
  • 在生产中运行应用程序时通常存在的所有JAR

这是我在使用log4j时所做的。如果使用其他日志记录实现,则相应地进行调整。如果你使用不同版本的slf4j并不重要,只要“API”和实现JAR是相同的版本(我的是1.6.0)。

答案 2 :(得分:5)

另一种解决方案是将日志记录实现切换为仅用于测试的简单实现。

所以在你的pom.xml中

    <!-- Depend on slf4j API -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.12</version>
    </dependency>

    <!-- Use SimpleLogger as the slf4j implementation in test -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-simple</artifactId>
        <version>1.7.12</version>
        <scope>test</scope>
    </dependency>

    <!-- Use log4j as the slf4j implementation during runtime (not test) -->
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-log4j12</artifactId>
        <version>1.7.12</version>
        <scope>runtime</scope>
    </dependency>

SimpleLogger默认情况下只将所有内容记录到stderr,不需要任何配置文件

答案 3 :(得分:2)

我们使用log4j作为输出记录器;

private static Logger log = LoggerFactory.getLogger(MyClassHere.class);

如果正确配置,slf4j应该找到并使用log4j而不会出现问题。 为了简化生活,我会使用这种Eclipse模式,因为您将编写这段代码:

private static Logger log = LoggerFactory.getLogger(${enclosing_type}.class);
${:import(org.slf4j.Logger,org.slf4j.LoggerFactory)}

对于测试,我建议你不要超过INFO级别,并将大部分内容保持为DEBUG。如果你真的想要智能地捕获大量错误,那么我建议你查看PositronLogger这是一个日志文件appender,它会默默地接收所有内容到TRACE,但只有在它捕获ERROR时才将其转储到文件中;有点像时间旅行:))

http://github.com/andreaja/PositronLogger

答案 4 :(得分:0)

如果使用log4j 1作为底层的slf4j实现

log4j默认情况下不会记录任何内容,除非你,

  • 在类路径上有一个public void addTextTry(){ JLabel l1 = new JLabel("The add method appends an element to an array."); JLabel l2 = new JLabel("This inturn increases the arrays size."); jPanel1.removeAll(); jPanel1.add(l1); jPanel1.add(l2); jPanel1.revalidate(); jPanel1.repaint(); } 文件
  • 设置log4j.properties。如果使用maven,可以在maven-surefire-plugin配置中完成。
  • 以编程方式设置log4j配置,比如@BeforeClass方法

    -Dlog4j.configuration=file:///path/to/log4j.properties

    这只是有效,但需要将其放在每个单元测试上,这很痛苦。所以我不推荐,除非你暂时需要它来快速获得一些工作。

答案 5 :(得分:0)

在编写新测试时添加日志记录非常有用。同时,当在CI或CD管道中运行测试时,您希望禁用日志记录(或至少不再详细)。有时故障是瞬态的,尤其是在端到端运行中,因此在CI作业的控制台日志中获得相关输出是有帮助的。本文非常清楚地描述了它 - https://gualtierotesta.wordpress.com/2015/11/01/tutorial-logging-during-tests/

答案 6 :(得分:0)

我不想设置xml文件,也不想使用System.out.println。通常,对于单元测试,我需要这样做。

您可以设置测试记录器:

<!-- Use SimpleLogger as the slf4j implementation in test -->
<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-simple</artifactId>
  <version>1.7.12</version>
  <scope>test</scope>
</dependency>

在创建记录器之前,请设置以下属性:(参考:http://www.slf4j.org/api/org/slf4j/impl/SimpleLogger.html除TRACE之外的其他可用属性)

static {
    System.setProperty("org.slf4j.simpleLogger.defaultLogLevel","TRACE");
}

创建记录器:

private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);


private void doSomeLogs(){
    LOGGER.trace("trace it"); 
}

答案 7 :(得分:0)

如果(允许)注入记录器,则可以使用。

testImplementation group: 'com.portingle', name: 'slf4jtesting', version: portingleSlf4jtestingVersion

https://github.com/portingle/slf4jtesting

您的课程看起来像这样:

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

import javax.inject.Inject;


public final class MyThing {

    private Logger logger;

    @Inject
    MyThingTest() {
        this(LoggerFactory
                .getLogger(MyThing.class));
    }

    public MyThing(final Logger lgr) {
        if (null == lgr) {
            throw new IllegalArgumentException("Logger is null");
        }
        this.logger = lgr;

    }

在单元测试代码中,您会有类似以下内容:

@Test
public void myTest() {

    TestLogger unitTestLogger = this.getUnitTestLogger();


    MyThing testItem = new MyThing(unitTestLogger);
   testItem.doSomethingWithLogs();
}


private TestLogger getUnitTestLogger() {
    TestLoggerFactory loggerFactory = Settings.instance()
            .enableAll() // necessary as by default only ErrorLevel is enabled
            .buildLogging();

    TestLogger returnItem = loggerFactory.getLogger(MyThingTest.class.getName());
    assertNotNull(returnItem);
    return returnItem;
}



// render nicer errors
private void assertLoggerContains(TestLogger unitTestLogger, LogLevel logLev, String expected) throws Error {
    if (!unitTestLogger.contains(logLev, expected)) {
        throw new AssertionError("expected '" + expected + "' but got '" + unitTestLogger.lines() + "'");
    }
}

// render nicer errors
private void assertLoggerNotContains(TestLogger unitTestLogger, LogLevel logLev, String expected) throws Error {
    if (unitTestLogger.contains(logLev, expected)) {
        throw new AssertionError("expected absence of '" + expected + "' but got '" + unitTestLogger.lines() + "'");
    }
}

答案 8 :(得分:-1)

这对我来说似乎是一种难闻的气味。

您应始终避免手动检查或验证单元测试。

单元测试应该是自动化的,只有当您的构建工具告诉您某些测试失败时才需要人工干预,并且应使用所有这些验证方法(例如assertEquals)提供失败原因