【发布时间】:2018-09-21 03:39:24
【问题描述】:
我正在使用 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 规范
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,但由于 logger 类是最终类,所以会出错。
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.
运行时的记录器类
package ch.qos.logback.classic;
public final class Logger implements org.slf4j.Logger, LocationAwareLogger, AppenderAttachable<ILoggingEvent>, Serializable {
谢谢
【问题讨论】:
-
请分享MCVE。 “我正在做与其他人相似的事情,但它并没有达到我的预期”几乎不能作为一个可以回答的问题。我喜欢查看您的 Spock 规范以及正在测试的代码,以便能够重现和理解您的问题。如果这要求太多,请单独解决您的问题。
-
我添加了一个 MCVE。
-
不,你没有。
-
抱歉,我错过了有关规范的部分。我现在添加了工作规范、虚拟类和助手。
标签: unit-testing logging groovy functional-programming spock