Spock单元测试断言日志调用并查看输出
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spock单元测试断言日志调用并查看输出相关的知识,希望对你有一定的参考价值。
我正在使用spock来测试Java Spring Boot代码。它获取了一个关于lombok @ Slf4j注释的logback记录器。
带日志调用的虚拟类
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
@Slf4j
@Component
public class Clazz {
public void method() {
// ... code
log.warn("message", new RuntimeException());
}
}
Spock Spec
import groovy.util.logging.Slf4j
import org.junit.Rule
import org.slf4j.Logger
import spock.lang.Specification
@Slf4j
class LogSpec extends Specification {
Clazz clazz = new Clazz()
private Logger logger = Mock(Logger.class)
@Rule
ReplaceSlf4jLogger replaceSlf4jLogger = new ReplaceSlf4jLogger(Clazz, logger)
def "warning ia logged"() {
given: "expected message"
when: "when calling the method"
clazz.method()
then: "a warning is logged"
1 * logger.warn(_, _) >> {
msg, ex -> log.warn(msg, ex)
}
}
}
Helper用this answer取得的模拟记录器切换真实。
import org.junit.rules.ExternalResource
import org.slf4j.Logger
import java.lang.reflect.Field
import java.lang.reflect.Modifier
/**
* Helper to exchange loggers set by lombok with mock logger
*
* allows to assert log action.
*
* Undos change after test to keep normal logging in other tests.
*
* code from this <a href="https://stackoverflow.com/a/25031713/3573038">answer</a> answer
*/
class ReplaceSlf4jLogger extends ExternalResource {
Field logField
Logger logger
Logger originalLogger
ReplaceSlf4jLogger(Class logClass, Logger logger) {
logField = logClass.getDeclaredField("log")
this.logger = logger
}
@Override
protected void before() throws Throwable {
logField.accessible = true
Field modifiersField = Field.getDeclaredField("modifiers")
modifiersField.accessible = true
modifiersField.setInt(logField, logField.getModifiers() & ~Modifier.FINAL)
originalLogger = (Logger) logField.get(null)
logField.set(null, logger)
}
@Override
protected void after() {
logField.set(null, originalLogger)
}
}
我想测试日志调用,但仍然看到日志消息。
我正在使用this answer的解决方案,它适用于断言,但我没有看到日志,因为它是一个模拟调用。
我提出了这个解决方案,它使用groovy规范的记录器进行调用。
1 * logger.warn(_ , _) >> {
msg, ex -> log.warn(msg, ex)
}
但我发现它很冗长,任何想法如何为它创建一个辅助函数。我不熟悉函数groovy并将此代码移动到函数中是行不通的。
我也尝试过Spy而不是Mock,但这会让我错误,因为记录器类是最终的。
import ch.qos.logback.classic.Logger
private Logger logger = Spy(Logger.class)
>> org.spockframework.mock.CannotCreateMockException: Cannot create mock
for class ch.qos.logback.classic.Logger because Java mocks cannot mock final classes.
If the code under test is written in Groovy, use a Groovy mock.
运行时的Logger类
package ch.qos.logback.classic;
public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {
谢谢
实际上在你的MCVE中你期望用两个参数调用warn(_, _)
方法,但是你没有在Clazz
中那样记录,所以要么你必须改变Clazz
来记录异常或者改变文本以期望用一个参数进行方法调用。我在这里做后者。
至于你的问题,解决方案是不使用模拟而是间谍。但是,你需要告诉Spock你想要监视哪个类。这是因为你当然不能监视接口类型。我选择了一个SimpleLogger
(更改为您在应用程序中使用的任何内容)。
package de.scrum_master.stackoverflow
import groovy.util.logging.Slf4j
import org.junit.Rule
import org.slf4j.impl.SimpleLogger
import spock.lang.Specification
@Slf4j
class LombokSlf4jLogTest extends Specification {
SimpleLogger logger = Spy(constructorArgs: ["LombokSlf4jLogTest"])
@Rule
ReplaceSlf4jLogger replaceSlf4jLogger = new ReplaceSlf4jLogger(Clazz, logger)
def "warning is logged"() {
when: "when calling the method"
new Clazz().method()
then: "a warning is logged"
1 * logger.warn(_)
}
}
更新:对于它的价值,这里有一个版本,它也适用于类路径上的LogBack-Classic而不是Log4J-Simple。不要直接监视最后的类,让我们只是窥探一个Groovy @Delegate
:
还请注意,我在测试中更改为*_
,以适应具有任意数量参数的warn
调用。
package de.scrum_master.stackoverflow
import groovy.util.logging.Slf4j
import org.junit.Rule
import org.slf4j.Logger
import spock.lang.Specification
@Slf4j
class LombokSlf4jLogTest extends Specification {
def logger = Spy(new LoggerDelegate(originalLogger: log))
@Rule
ReplaceSlf4jLogger replaceSlf4jLogger = new ReplaceSlf4jLogger(Clazz, logger)
def "warning is logged"() {
when: "when calling the method"
new Clazz().method()
then: "a warning is logged"
1 * logger.warn(*_)
true
}
static class LoggerDelegate {
@Delegate Logger originalLogger
}
}
以上是关于Spock单元测试断言日志调用并查看输出的主要内容,如果未能解决你的问题,请参考以下文章