我在我的项目中使用 JUnit4 和 Hibernate3。Hibernate 依赖于 Slf4j,因此我的项目也包含这个库。现在我想在单元测试中使用 Slf4j 来记录补充测试信息。您能否提供一个简短的示例来说明我的单元测试应该是什么样子以便只记录一行文本?最好在多个测试中没有代码重复。
9 回答
为什么要在单元测试中记录内容?单元测试应该是通过/失败,并且应该使用测试框架来表明这一点。您不想通过输出来查看测试是通过还是失败。如果它失败了,在你的 IDE/调试器中运行它是修复它的最好方法。
我也喜欢在我的 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)。
另一种解决方案是将日志记录实现切换为仅用于测试的简单实现。
所以在你的 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,不需要任何配置文件
我们使用 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,它是一个日志文件附加程序,它将默默地收集所有内容到 TRACE,但只有在捕获 ERROR 时才将其转储到文件中;有点像时间旅行:)
如果使用 log4j 1 作为底层 slf4j 实现
log4j 默认不记录任何东西,除非你,
- 在类路径上有一个
log4j.properties
文件 - 设置
-Dlog4j.configuration=file:///path/to/log4j.properties
。如果使用 maven,这可以在您的 maven-surefire-plugin 配置中完成。 以编程方式设置 log4j 配置,例如在 @BeforeClass 方法中
@BeforeClass public static void beforeClass() { BasicConfigurator.resetConfiguration(); BasicConfigurator.configure(); }
这很有效,但是需要将它放在每个单元测试中,这很痛苦。所以我不推荐,除非你暂时需要它来快速开始工作。
在编写新测试时添加日志记录很有用。同时,当测试在 CI 或 CD 管道中运行时,您希望禁用日志记录(或至少不那么冗长)。有时故障是暂时的,尤其是在端到端运行中,因此在 CI 作业的控制台日志上提供相关输出是有帮助的。这篇文章很好地描述了它 - https://gualtierotesta.wordpress.com/2015/11/01/tutorial-logging-during-tests/
我不想设置 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>
在创建 Logger 之前设置此属性:(参考: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");
}
如果您(允许)注入记录器,则可以使用。
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() + "'");
}
}
对我来说,这似乎是一种难闻的气味。
您应该始终避免手动检查或验证单元测试。
单元测试应该是自动化的,只有当您的构建工具告诉您某些测试失败时才需要人工干预,并且应该使用所有这些验证方法(例如 assertEquals)提供失败原因