想要做似乎是一件合理的事情......例如,当测试失败时.log某事,如果没有失败则不然。
例如,我从2013年开始发现这一点......当时没有简单有效的答案。 现在呢?
我希望可以在org.spockframework.runtime.SpecificationContext
中找到合适的属性/方法......或者也许org.spockframework.runtime.model.SpecInfo
...但我什么也看不见。
后
回答我可能想做的事情的问题:事实上,我的Specification
"劫持"System.out
(使用PrintStream
(,所以我可以捕获输出到System.out
然后分析它。 注意记录在案,我知道 Spock 纯粹主义者可能不赞成对终端输出感兴趣的测试,但我不是那么纯粹主义者,特别是在谈论单元测试以外的测试时。
获得此输出的方式意味着它不会在任何地方输出,没有理由系统地记录它并使日志文件混乱......但如果测试失败,我想这样做。同上,可能System.err
...
首先,记录失败的测试是测试框架(JUnit,Spock(的工作。因此,测试本身的状态在测试本身中不容易获得也就不足为奇了。无论如何,如果你想在斯波克中得到一些更花哨的东西,那么接受的答案以及彼得在另一个线程中的答案仍然有效。没有更简单的方法可以从cleanup()
方法中找出测试是否失败。
无论如何,它并不像看起来那么复杂,因为您只设置一次,然后它就可以工作了。由于您没有提到您究竟要登录cleanup()
什么,我将进行一些推测。出于演示目的,我只是记录从规范上下文中检索的功能方法名称,以及已发生的错误的类(以免两次打印整个花哨的 Spock 错误消息(,这些是从我将在此处介绍的全局扩展注册的运行侦听器中检索的。
全球斯波克扩展:
该扩展注册一个运行侦听器,该侦听器在发生测试错误时记录错误信息。在每个特征或迭代开始时(对于具有where:
块的特征(,最后记录的错误将被清除,以免渗入下一个特征/迭代。
package de.scrum_master.testing.extension
import org.spockframework.runtime.AbstractRunListener
import org.spockframework.runtime.extension.AbstractGlobalExtension
import org.spockframework.runtime.model.ErrorInfo
import org.spockframework.runtime.model.IterationInfo
import org.spockframework.runtime.model.SpecInfo
class TestResultExtension extends AbstractGlobalExtension {
@Override
void visitSpec(SpecInfo spec) {
spec.addListener(new ErrorListener())
}
static class ErrorListener extends AbstractRunListener {
ErrorInfo errorInfo
@Override
void beforeIteration(IterationInfo iteration) {
errorInfo = null
}
@Override
void error(ErrorInfo error) {
errorInfo = error
}
}
}
如何注册 Spock 扩展:
还需要将文件META-INF/services/org.spockframework.runtime.extension.IGlobalExtension
添加到测试资源中,以便注册扩展。该文件仅包含以下内容:
de.scrum_master.testing.extension.TestResultExtension
顺便说一句,这不是Spock或Groovy的东西,而是一个标准的Java SE功能,称为服务提供商。
使用扩展的示例测试:
这个测试非常愚蠢,但显示了它如何适用于普通方法和具有where:
块的方法,无论有无@Unroll
。
package de.scrum_master.testing.extension
import spock.lang.Specification
import spock.lang.Unroll
class TestFailureReportingTest extends Specification {
def "failing normal feature"() {
expect:
0 == 1
}
def "passing normal feature"() {
expect:
0 == 0
}
def "parametrised feature"() {
expect:
a == b
where:
a << [2, 4, 6]
b << [3, 5, 6]
}
@Unroll
def "unrolled feature with #a/#b"() {
expect:
a == b
where:
a << [6, 8, 0]
b << [7, 9, 0]
}
def cleanup() {
specificationContext.currentSpec.listeners
.findAll { it instanceof TestResultExtension.ErrorListener }
.each {
def errorInfo = (it as TestResultExtension.ErrorListener).errorInfo
if (errorInfo)
println "Test failure in feature '${specificationContext.currentIteration.name}', " +
"exception class ${errorInfo.exception.class.simpleName}"
else
println "Test passed in feature '${specificationContext.currentIteration.name}'"
}
}
}
控制台日志(省略实际错误(将为:
Test failure in feature 'failing normal feature', exception class ConditionNotSatisfiedError
Test passed in feature 'passing normal feature'
Test failure in feature 'parametrised feature', exception class ConditionNotSatisfiedError
Test failure in feature 'parametrised feature', exception class ConditionNotSatisfiedError
Test passed in feature 'parametrised feature'
Test failure in feature 'unrolled feature with 6/7', exception class ConditionNotSatisfiedError
Test failure in feature 'unrolled feature with 8/9', exception class ConditionNotSatisfiedError
Test passed in feature 'unrolled feature with 0/0'
PS:错误信息在功能方法内的cleanup:
块中尚不可用,因为扩展仅在包括该块在内的整个功能/迭代完成后启动。所以你真的必须使用一个cleanup()
的方法,但无论如何你都想要它,它避免了代码重复。
P.P.S.:当然,您也可以从方法拦截器中进行通用日志记录,并跳过整个cleanup()
方法。但是,您不再可以使您的日志输出特定于测试,它将用于您的所有测试,而不仅仅是您选择的测试 - 当然,除非您将软件包或规范名称过滤器硬编码到拦截器中,或者确保拦截器在 Spock 启动时读取相应的配置文件。