我有下面的Logger,我想模拟出来,但要验证日志条目被调用,而不是内容。
private static Logger logger =
LoggerFactory.getLogger(GoodbyeController.class);
字符串
我想模拟任何用于LoggerFactory.getLogger()的类,但我不知道如何做到这一点。这就是我到目前为止所做的:
@Before
public void performBeforeEachTest() {
PowerMockito.mockStatic(LoggerFactory.class);
when(LoggerFactory.getLogger(GoodbyeController.class)).
thenReturn(loggerMock);
when(loggerMock.isDebugEnabled()).thenReturn(true);
doNothing().when(loggerMock).error(any(String.class));
...
}
型
我想知道:
1.我可以模拟静态LoggerFactory.getLogger()
来为任何类工作吗?
1.我似乎只能在@Before
中运行when(loggerMock.isDebugEnabled()).thenReturn(true);
,因此我似乎无法更改每个方法的特征。有办法解决吗?
编辑调查结果:
我想我已经试过了,它不起作用:
when(LoggerFactory.getLogger(any(Class.class))).thenReturn(loggerMock);
型
谢谢你,因为它起作用了。
然而,我尝试了无数的变化:
when(loggerMock.isDebugEnabled()).thenReturn(true);
型
我不能让loggerMock在@Before
之外改变它的行为,但这只发生在Coburtura上。与三叶草,覆盖率显示100%,但仍然有一个问题,无论哪种方式。
我有一个简单的类:
public ExampleService{
private static final Logger logger =
LoggerFactory.getLogger(ExampleService.class);
public String getMessage() {
if(logger.isDebugEnabled()){
logger.debug("isDebugEnabled");
logger.debug("isDebugEnabled");
}
return "Hello world!";
}
...
}
型
然后我做了这个测试:
@RunWith(PowerMockRunner.class)
@PrepareForTest({LoggerFactory.class})
public class ExampleServiceTests {
@Mock
private Logger loggerMock;
private ExampleServiceservice = new ExampleService();
@Before
public void performBeforeEachTest() {
PowerMockito.mockStatic(LoggerFactory.class);
when(LoggerFactory.getLogger(any(Class.class))).
thenReturn(loggerMock);
//PowerMockito.verifyStatic(); // fails
}
@Test
public void testIsDebugEnabled_True() throws Exception {
when(loggerMock.isDebugEnabled()).thenReturn(true);
doNothing().when(loggerMock).debug(any(String.class));
assertThat(service.getMessage(), is("Hello null: 0"));
//verify(loggerMock, atLeast(1)).isDebugEnabled(); // fails
}
@Test
public void testIsDebugEnabled_False() throws Exception {
when(loggerMock.isDebugEnabled()).thenReturn(false);
doNothing().when(loggerMock).debug(any(String.class));
assertThat(service.getMessage(), is("Hello null: 0"));
//verify(loggerMock, atLeast(1)).isDebugEnabled(); // fails
}
}
型
在三叶草中,我展示了if(logger.isDebugEnabled()){
块的100%覆盖率。但是如果我尝试验证loggerMock
:
verify(loggerMock, atLeast(1)).isDebugEnabled();
型
我没有任何互动。我也试过PowerMockito.verifyStatic()
;在@Before
中,但是也具有零交互。
这看起来很奇怪,Cobertura显示if(logger.isDebugEnabled()){
没有100%完成,而三叶草显示了,但两者都同意验证失败。
9条答案
按热度按时间mrfwxfqh1#
编辑2020-09-21:自3.4.0以来,Mockito支持模拟静态方法,API仍在孵化中,可能会发生变化,特别是在存根和验证方面。它需要
mockito-inline
工件。您不需要准备测试或使用任何特定的跑步者。你需要做的就是:字符串
这段代码中的两个重要方面是,您需要在应用静态模拟时确定范围,即:在这个尝试块中。您需要从
MockedStatic
对象调用存根和验证API。@Mick,试着让静态字段的所有者也做好准备,例如:
型
编辑1:我只是制作了一个小例子。首先是控制器:
型
然后是测试:
型
注意进口!类路径中值得注意的库:Mockito、PowerMock、JUnit、logback-core、logback-clasic、slf4j
EDIT 2:由于这似乎是一个流行的问题,我想指出,如果这些日志消息是那么重要,需要测试,即.它们是系统的功能/业务部分**,那么引入一个真实的的依赖关系,使这些日志成为功能,在整个系统设计中会更好**,而不是依赖于标准的静态代码和日志记录器的技术类。
对于这个问题,我建议使用
reportIncorrectUseOfYAndZForActionX
或reportProgressStartedForActionX
之类的方法来创建一个类似于=Reporter
的类。这样做的好处是,任何阅读代码的人都可以看到该特性。但它也将有助于实现测试,更改此特定功能的实现细节.因此,您不需要像PowerMock这样的静态模拟工具。在我看来,静态代码可以很好,但一旦测试需要验证或模拟静态行为,就有必要重构并引入清晰的依赖关系。
gjmwrych2#
派对有点晚了-我正在做类似的事情,需要一些指点,最后来到了这里。没有功劳--我从Brice那里拿走了所有的代码,但得到的“零交互”比Cengiz得到的要少。
使用jheriks和Joseph Lust的指导,我想我知道为什么-我把我的对象作为一个字段进行测试,并在@Before中更新它,不像Brice。那么实际的记录器就不是mock,而是一个真实的的类,就像jhriks建议的那样初始化了……
我通常会为我的被测对象这样做,以便为每个测试获得一个新的对象。当我把字段移到本地并在测试中更新时,它运行正常。然而,如果我尝试第二个测试,它不是我的测试中的模拟,而是第一个测试中的模拟,我再次得到零交互。
当我把mock的创建放在@BeforeClass中时,被测对象中的logger总是mock,但请参阅下面的注解以了解此问题...
测试类
字符串
测试
型
备注
如果你有两个具有相同期望的测试,我必须在@AfterClass中进行验证,因为静态调用是堆叠的-
verify(mockLOG, times(2)).info("true");
-而不是在每个测试中进行times(1),因为第二个测试会失败,说这里有2个调用。这是漂亮的裤子,但我找不到一种方法来清除调用。我想知道有没有人能想出一个解决这个问题的办法...k0pti3hp3#
在回答你的第一个问题时,它应该像替换一样简单:
字符串
与
型
关于你的第二个问题(可能还有第一个令人困惑的行为),我认为问题在于logger是静态的。所以
型
在 class 初始化时执行,而不是在 object 示例化时执行。有时这可能是在同一时间,所以你会没事,但很难保证。因此,您设置LoggerFactory.getLogger来返回您的模拟,但是在设置模拟时,logger变量可能已经用一个真实的的Logger对象设置了。
您可以使用ReflectionTestUtils之类的工具显式地设置日志记录器(我不知道这是否适用于静态字段),或者将其从静态字段更改为示例字段。无论哪种方式,都不需要模拟LoggerFactory.getLogger,因为您将直接注入模拟Logger示例。
64jmpszr4#
我认为您可以使用Mockito.reset(mockLog)重置调用。您应该在每次测试之前调用它,所以在@Before内部是一个好地方。
gr8qqesn5#
字符串
JUNIT测试覆盖率-
型
ss2ws0br6#
使用显式注入。没有其他方法允许您在同一JVM中并行运行测试。
使用类加载器的模式,如静态日志绑定器,或与环境思想相混淆的模式,如logback.XML,在测试时都是失败的。
考虑一下我提到的并行化测试,或者考虑一下您想要拦截组件A的日志记录的情况,组件A的构造隐藏在API之后如果您使用的是从顶部注入的依赖项loggerfactory,则后一种情况很容易处理,但如果您注入Logger,则不容易处理,因为在ILoggerFactory. getLogger的该程序集中没有接缝。
也不全是单元测试。有时我们希望集成测试发出日志记录。有时候我们不会。我们希望一些集成测试日志记录被选择性地抑制,例如预期的错误,否则会使CI控制台混乱和混乱。如果您从您的主线(或您可能使用的任何di框架)的顶部注入ILoggerFactory,那么一切都很容易
所以...
按照建议注入报告程序或采用注入ILoggerFactory的模式。通过显式的ILoggerFactory注入而不是Logger,您可以支持许多访问/拦截模式和并行化。
vddsk6oq7#
下面是一个测试类,它模拟类
LogUtil
中名为log
的私有静态final Logger。除了模拟
getLogger
工厂调用之外,还需要在@BeforeClass
中使用反射显式地设置字段字符串
qf9go6mv8#
我已经让它在没有
PowerMock
的情况下工作,而Mockito
只用于一个测试。当我用verify
进行两个测试时,当我运行类中的所有测试时,第二个用no interactions with this mock
开始失败。两个测试分别起作用。经过一些实验后,只有当我制作模拟日志记录器
static
时,它才对我有效。字符串
yiytaume9#
使用白盒的简单解决方案:
字符串
的数据