【发布时间】:2013-11-25 14:56:06
【问题描述】:
我有一个包含 2 个条件的方法。在每种情况下都会调用 Logger.error 方法。验证该方法调用的第一个测试成功,但任何其他测试都失败了
需要但未调用...实际上,与 这个模拟。
有人知道为什么会这样吗?
下面,我提供了一个示例类和一个会产生问题的单元测试:
package packageName;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class X {
private static final Logger LOGGER = LoggerFactory.getLogger(X.class);
public void execute(boolean handle1stCase) {
if (handle1stCase) {
LOGGER.error("rumpampam");
} else {
LOGGER.error("latida");
}
}
}
测试:
package packageName;
import org.apache.commons.logging.LogFactory;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
import static org.powermock.api.mockito.PowerMockito.mockStatic;
@RunWith(PowerMockRunner.class)
@PrepareForTest({LoggerFactory.class})
public class XTest {
@Mock
private Logger loggerMock;
private X x;
@Before
public void construct() {
MockitoAnnotations.initMocks(this);
mockStatic(LoggerFactory.class);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMock);
x = new X();
}
@Test
public void whenFirstCaseErrorLogged() throws Exception {
x.execute(true);
verify(loggerMock, times(1)).error("rumpampam");
}
@Test
public void whenSecondCaseErrorLogged() throws Exception {
x.execute(false);
verify(loggerMock, times(1)).error("latida");
}
}
结果:
需要但未调用:loggerMock.error("latida"); -> 在 packageName.XTest.whenSecondCaseErrorLogged(XTest.java:51)
实际上,与此模拟的交互为零。
编辑:
我简短地回答了为什么除了第一个测试之外的每个测试都在comment of this answer 中失败。
我的问题解决方案:
在测试中提供一个:
public static Logger loggerMockStatic;
比只为所有测试创建一个实例并在静态变量中提供它,然后使用静态 loggerMockStatic 从比开始。所以你会有:
...
MockitoAnnotations.initMocks(this);
if (loggerMockStatic == null) {
loggerMockStatic = loggerMock;
}
mockStatic(LoggerFactory.class);
//when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMock);
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMockStatic);
...
并在验证方法中使用 loggerMockStatic 而不是 loggerMock。
关于方法的一些想法:
对我来说这很好,因为
1. 它不会破坏设计(如果您认为所需的变量应该是一个常量,那么它将保持这种状态)。
2. 在测试中添加的仅 4 行将允许您测试常量(在本例中为记录器)行为。污染不多,测试用例还是很清楚的。
我在this answer 中解释的“删除final 并提供setter”方法会使系统容易受到攻击。不需要有人将记录器设置为类,我总是希望系统根据需要打开。不希望只为测试需要提供设置器。测试应该适用于实现,而不是相反。
特别是在测试日志记录时,我不认为应该在一般(大多数)情况下测试日志记录。日志记录应该是应用程序的一个方面。当您有其他输出要测试某个路径时,应该测试这些输出。但是在这种情况下(可能还有其他情况),如果某个路径没有其他输出,例如在某个条件下记录和返回,则需要测试日志(根据我的说法)。我想始终知道即使有人更改条件,日志消息仍将被记录。如果没有日志,并且如果有人以错误的方式更改条件,则无法知道错误存在于这段代码中(可能调试除外)。
我正在与一些同事讨论,有一个单独的类来进行日志记录可以解决问题。这样,常量在另一个类中被隔离,您将能够仅使用 Mockito 检查行为。他们进一步评论说,如果您想将日志发送到电子邮件,这样会更容易更改。
首先,如果您不打算在不久的将来在日志记录方式之间切换,我认为这是一种过早的模块化。
其次,仅使用 Mockito + 具有另一个类和 + 3 行代码 VS 我的一行代码 (logger.error(...)) + 使用 PowerMockito,我将再次使用后者。在测试期间添加额外的依赖项不会使您的生产代码变慢和变大。也许在考虑持续集成并且测试也与其他阶段一样重要时,您可能会说这会使测试过程变得更慢、更庞大,但我会牺牲这一点——这对我来说似乎没什么大不了的。
【问题讨论】:
标签: java unit-testing logging mockito powermock