我正在使用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函数可以用来切换从这个答案中获取的真实记录器和模拟记录器。
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://dev59.com/lV8f5IYBdhLWcg3wHPiN#25031713">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)
}
}
我想测试日志调用,但仍然想看到日志消息。
我使用了这个答案中的解决方案,它对于断言有效,但由于是模拟调用,我看不到日志。
我想出了这个解决方案,它使用groovy规范的记录器进行调用。
1 * logger.warn(_ , _) >> {
msg, ex -> log.warn(msg, ex)
}
但我觉得这个代码有点啰嗦,你有什么想法可以创建一个辅助函数吗?我对函数式Groovy不是很熟悉,将此代码移入一个函数中也行不通。
我还尝试使用Spy而不是Mock,但由于logger类是final的,所以出现了错误。
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 {
谢谢