如何在 Spock 的私有静态最终变量中使用 mock?
How to use mock in a private static final variable in Spock?
假设我在 Java class 中有一个 private static final
变量,例如:
@Service
public class MyClass {
private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
...
}
然后我有测试 class 比如:
class MyClassTest extends Specification {
@Autowired
MyClass sut
def "Testing a private static final variable"() {
given:
sut.LOGGER = Mock(Logger)
when:
...
}
如何将 Mock(Logger)
与 sut.LOGGER
一起使用,其中 LOGGER
是 MyClass
class 中的 private static final
?如果没有 final
关键字,测试将 运行 正常,但我希望它与 final
关键字一起使用。非常感谢任何建议。
假设我们不希望使用 Mockito 内联 mock maker 或其他 mocking 强大的工具,而只是使用 Spock 的 on-board 手段,我们可以使用另一种专门针对 Slf4j 的方法:使用现有的记录器而不是 mocking它,但添加一个模拟 appender,然后验证那个上的交互。唯一的要求是知道使用了哪个实际的日志记录工具,或者只是配置测试环境以使用特定的工具。让我们选择 LogBack。如果我们知道 Slf4J 记录到 LogBack,我们可以导入它 类 并将记录器转换为它。
Class 测试中:
我故意弄得复杂一点,所以后面我们可以测试正数、负数和零数的几种情况:
package de.scrum_master.Whosebug.q71235231;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClassWithLogger {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassWithLogger.class);
public void logSomething(int i) {
LOGGER.info("FYI, 3 * {} = {}", i, 3 * i);
if (i < 0)
LOGGER.warn("Be warned that {} is a negative number", i);
if (i == 0)
LOGGER.error("Using zero is forbidden");
}
}
斯波克测试
package de.scrum_master.Whosebug.q71235231
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.spi.LoggingEvent
import ch.qos.logback.core.Appender
import spock.lang.Specification
import spock.lang.Unroll
class ClassWithLoggerTest extends Specification {
@Unroll("test logger with number #number")
def "test logger"() {
given:
// Groovy can simply access private fields, if we know their names
Logger logger = ClassWithLogger.LOGGER
def appender = Mock(Appender)
logger.addAppender(appender)
when:
new ClassWithLogger().logSomething(number)
then:
1 * appender.doAppend({ LoggingEvent event ->
event.level == Level.INFO &&
event.formattedMessage == "FYI, 3 * $number = ${3 * number}"
})
(number == 0 ? 1 : 0) * appender.doAppend({ LoggingEvent event ->
event.level == Level.ERROR &&
event.formattedMessage == "Using zero is forbidden"
})
(number < 0 ? 1 : 0) * appender.doAppend({ LoggingEvent event ->
event.level == Level.WARN &&
event.formattedMessage == "Be warned that $number is a negative number"
})
cleanup:
logger.detachAppender(appender)
where:
number << [4, 1, 0, -1, -7]
}
}
像(number < 0 ? 1 : 0) * ...
这样的动态交互计数可读性不强。我只是想展示 Spock 在测试参数化方面的能力,并在一个测试方法中涵盖所有场景。也许您最好将它分成 3 种方法并分别介绍每种情况。这样测试会更具可读性,但你也会有更多的重复代码。这是一个品味问题,我把选择权交给你。
更新: 如果您希望在 where:
部分计算更多动态内容(也可以在 given:
中完成),您可以使用这个变体(只是化妆品,逻辑不变):
class ClassWithLoggerTest extends Specification {
@Unroll("test logger with number #number")
def "test logger"() {
given:
// Groovy can simply access private fields, if we know their names
Logger logger = ClassWithLogger.LOGGER
def appender = Mock(Appender)
logger.addAppender(appender)
when:
new ClassWithLogger().logSomething(number)
then:
infoCount * appender.doAppend({ LoggingEvent event ->
event.level == Level.INFO && event.formattedMessage == infoMessage
})
errorCount * appender.doAppend({ LoggingEvent event ->
event.level == Level.ERROR && event.formattedMessage == errorMessage
})
warningCount * appender.doAppend({ LoggingEvent event ->
event.level == Level.WARN && event.formattedMessage == warningMessage
})
cleanup:
logger.detachAppender(appender)
where:
number << [4, 1, 0, -1, -7]
infoCount = 1
infoMessage = "FYI, 3 * $number = ${3 * number}"
errorCount = number == 0 ? 1 : 0
errorMessage = "Using zero is forbidden"
warningCount = number < 0 ? 1 : 0
warningMessage = "Be warned that $number is a negative number"
}
}
来源: 博客 post "Don't mock static: test SLF4J Logger with appenders",改编为 Spock。
假设我在 Java class 中有一个 private static final
变量,例如:
@Service
public class MyClass {
private static final Logger LOGGER = LoggerFactory.getLogger(MyClass.class);
...
}
然后我有测试 class 比如:
class MyClassTest extends Specification {
@Autowired
MyClass sut
def "Testing a private static final variable"() {
given:
sut.LOGGER = Mock(Logger)
when:
...
}
如何将 Mock(Logger)
与 sut.LOGGER
一起使用,其中 LOGGER
是 MyClass
class 中的 private static final
?如果没有 final
关键字,测试将 运行 正常,但我希望它与 final
关键字一起使用。非常感谢任何建议。
假设我们不希望使用 Mockito 内联 mock maker 或其他 mocking 强大的工具,而只是使用 Spock 的 on-board 手段,我们可以使用另一种专门针对 Slf4j 的方法:使用现有的记录器而不是 mocking它,但添加一个模拟 appender,然后验证那个上的交互。唯一的要求是知道使用了哪个实际的日志记录工具,或者只是配置测试环境以使用特定的工具。让我们选择 LogBack。如果我们知道 Slf4J 记录到 LogBack,我们可以导入它 类 并将记录器转换为它。
Class 测试中:
我故意弄得复杂一点,所以后面我们可以测试正数、负数和零数的几种情况:
package de.scrum_master.Whosebug.q71235231;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ClassWithLogger {
private static final Logger LOGGER = LoggerFactory.getLogger(ClassWithLogger.class);
public void logSomething(int i) {
LOGGER.info("FYI, 3 * {} = {}", i, 3 * i);
if (i < 0)
LOGGER.warn("Be warned that {} is a negative number", i);
if (i == 0)
LOGGER.error("Using zero is forbidden");
}
}
斯波克测试
package de.scrum_master.Whosebug.q71235231
import ch.qos.logback.classic.Level
import ch.qos.logback.classic.Logger
import ch.qos.logback.classic.spi.LoggingEvent
import ch.qos.logback.core.Appender
import spock.lang.Specification
import spock.lang.Unroll
class ClassWithLoggerTest extends Specification {
@Unroll("test logger with number #number")
def "test logger"() {
given:
// Groovy can simply access private fields, if we know their names
Logger logger = ClassWithLogger.LOGGER
def appender = Mock(Appender)
logger.addAppender(appender)
when:
new ClassWithLogger().logSomething(number)
then:
1 * appender.doAppend({ LoggingEvent event ->
event.level == Level.INFO &&
event.formattedMessage == "FYI, 3 * $number = ${3 * number}"
})
(number == 0 ? 1 : 0) * appender.doAppend({ LoggingEvent event ->
event.level == Level.ERROR &&
event.formattedMessage == "Using zero is forbidden"
})
(number < 0 ? 1 : 0) * appender.doAppend({ LoggingEvent event ->
event.level == Level.WARN &&
event.formattedMessage == "Be warned that $number is a negative number"
})
cleanup:
logger.detachAppender(appender)
where:
number << [4, 1, 0, -1, -7]
}
}
像(number < 0 ? 1 : 0) * ...
这样的动态交互计数可读性不强。我只是想展示 Spock 在测试参数化方面的能力,并在一个测试方法中涵盖所有场景。也许您最好将它分成 3 种方法并分别介绍每种情况。这样测试会更具可读性,但你也会有更多的重复代码。这是一个品味问题,我把选择权交给你。
更新: 如果您希望在 where:
部分计算更多动态内容(也可以在 given:
中完成),您可以使用这个变体(只是化妆品,逻辑不变):
class ClassWithLoggerTest extends Specification {
@Unroll("test logger with number #number")
def "test logger"() {
given:
// Groovy can simply access private fields, if we know their names
Logger logger = ClassWithLogger.LOGGER
def appender = Mock(Appender)
logger.addAppender(appender)
when:
new ClassWithLogger().logSomething(number)
then:
infoCount * appender.doAppend({ LoggingEvent event ->
event.level == Level.INFO && event.formattedMessage == infoMessage
})
errorCount * appender.doAppend({ LoggingEvent event ->
event.level == Level.ERROR && event.formattedMessage == errorMessage
})
warningCount * appender.doAppend({ LoggingEvent event ->
event.level == Level.WARN && event.formattedMessage == warningMessage
})
cleanup:
logger.detachAppender(appender)
where:
number << [4, 1, 0, -1, -7]
infoCount = 1
infoMessage = "FYI, 3 * $number = ${3 * number}"
errorCount = number == 0 ? 1 : 0
errorMessage = "Using zero is forbidden"
warningCount = number < 0 ? 1 : 0
warningMessage = "Be warned that $number is a negative number"
}
}
来源: 博客 post "Don't mock static: test SLF4J Logger with appenders",改编为 Spock。