I有一个包含两个条件的方法。在每个条件下,调用Logger.error方法。验证该方法调用的第一个测试成功,但任何其他测试都在 中失败。
想要但不是invoked...Actually,与这个模拟的交互是零的。
,有人知道为什么会这样吗?
下面,我提供了一个示例类和一个将产生问题的单元测试:
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"); }
结果:
通缉但未被调用: packageName.XTest.whenSecondCaseErrorLogged(XTest.java:51)的loggerMock.error("latida");-> 实际上,这个模拟没有任何交互作用。
编辑:
我给出了一个简短的答案,为什么除了第一次考试外,所有的考试都没有通过 comment of this answer 考试。
我的问题解决方案
在测试中提供:
public static Logger loggerMockStatic;
而不是为所有测试创建一个实例,并在静态变量中提供该实例,并从Than开始使用静态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。
关于 方法的几点思考
对我来说这很好因为
正如我在 this answer 中解释的那样,“”方法为系统打开了漏洞。不需要有人将记录器设置为类,我总是希望系统按需要打开。只为测试需要提供一个setter是不需要的。测试应该适用于实现,而不是相反。
特别是在测试日志记录方面,我不认为日志记录应该在一般情况下(大多数情况下)进行测试。日志应该是应用程序的一个方面。当您有其他输出要测试某一路径时,这些输出应该被测试。但是在这种情况下(或者其他情况下),在某个路径上没有其他输出,比如日志记录和在特定条件下返回,就需要测试日志(据我说)。我希望始终知道,即使有人更改了条件,日志消息仍将被记录。如果没有日志,如果有人以错误的方式更改条件,就无法知道错误存在于这段代码中(调试除外)。
我和一些同事讨论过,有一个单独的类来进行日志记录就可以做到这一点。这样,常量就被隔离在另一个类中,您将能够只使用Mockito来检查行为。他们进一步指出,这样如果你想把日志发送到电子邮件中,就更容易改变了。
首先,如果您不打算在不久的将来在日志记录方式之间切换,我认为这是一个过早的模块化。
其次,仅使用Mockito +有另一个类和+3行代码与我的一行代码(logger.error(.)+使用PowerMockito )+,我将再次使用后者。在测试期间添加额外的依赖项不会使您的生产代码变得更慢和更大。也许在考虑继续集成和测试与其他阶段一样重要时,您可能会说这会使测试过程变得更慢和更笨重,但我会牺牲这一点--对我来说,这似乎不是什么大问题。
【玩转 GPU】有奖征文
精美礼品等你拿!
您的记录器是静态的,因此它是在加载类时加载的,而不是在初始化对象时加载的。你没有保证你的模拟能准时完成,有时可能会起作用,有时不起作用。
向类 X 添加一个方法,以允许设置记录器,并从其中删除 final 。那就在考试中做这样的事。
X
final
@Mock private Logger mockLogger; private X toTest = new X(); @Before public void setUp() throws Exception { toTest.setLogger(mockLogger); @Test public void logsRumpampamForFirstCall() throws Exception { toTest.execute(true); verify(mockLogger).error("rumpampam");