代码之家  ›  专栏  ›  技术社区  ›  mike rodent

在specification.cleanup()中查找测试的结果/状态

  •  1
  • mike rodent  · 技术社区  · 6 年前

    想做这件事似乎是合情合理的…例如,当测试失败时记录一些东西,如果不成功则不记录。

    我发现了 this 例如,从2013年开始……当时没有既简单又有效的答案。现在呢?

    我希望能在 org.spockframework.runtime.SpecificationContext …或者也许 org.spockframework.runtime.model.SpecInfo …但我什么也看不见。

    后来

    回答我想做的事情的问题:事实上 Specification S“劫持” System.out (使用 PrintStream )所以我可以捕获输出到 系统输出 然后分析。注意,据我所知,spock纯粹主义者可能不赞成对终端输出感兴趣的测试,但我不是这样一个纯粹主义者,特别是在谈到单元测试以外的测试时。

    在获得这个输出的方式,这意味着它不输出任何地方,没有理由系统地记录它和混乱的日志文件…但如果测试失败,我想这样做。同上,可能用于 System.err

    1 回复  |  直到 6 年前
        1
  •  1
  •   kriegaex    6 年前

    首先,记录失败的测试是测试框架(junit,spock)的工作。因此,测试本身的状态并不容易从测试本身获得,这并不奇怪。不管怎样,如果你想在斯波克有更华丽的东西, accepted answer 还有 Peter's answer 在另一个线程中仍然有效。从 cleanup() 方法。

    无论如何,它并不像看上去那么复杂,因为你只设置了一次,然后它就工作了。因为你没有提到你到底想登录什么 清除() ,我要推测一下。出于演示的目的,我只是记录从规范上下文中检索的特性方法名,以及发生的错误的类(这样就不会打印整个花式spock错误消息两次),从由全局扩展注册的运行侦听器中检索,我将展示艾尔。

    全球Spock扩展:

    扩展注册了一个运行监听器,该监听器在测试错误发生时记录错误信息。在每个特性或迭代开始时(对于具有 where: 块)清除最后记录的错误,以免渗入下一个特征/迭代。

    package de.scrum_master.stackoverflow
    
    import org.spockframework.runtime.AbstractRunListener
    import org.spockframework.runtime.extension.AbstractGlobalExtension
    import org.spockframework.runtime.model.ErrorInfo
    import org.spockframework.runtime.model.FeatureInfo
    import org.spockframework.runtime.model.IterationInfo
    import org.spockframework.runtime.model.SpecInfo
    
    class MyGlobalExtension extends AbstractGlobalExtension {
      @Override
      void visitSpec(SpecInfo spec) {
        spec.addListener(new ErrorListener())
      }
    
      static class ErrorListener extends AbstractRunListener {
        ErrorInfo errorInfo
    
        @Override
        void beforeFeature(FeatureInfo feature) {
          errorInfo = null
        }
    
        @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.stackoverflow.MyGlobalExtension
    

    顺便说一下,这不是斯波克或Groovy的东西,而是标准的JavaSE特性。 service providers

    使用扩展名的示例测试:

    这个测试非常愚蠢,但是它展示了对于普通方法和 哪里: 有或没有积木 @Unroll .

    package de.scrum_master.stackoverflow
    
    import spock.lang.Specification
    import spock.lang.Unroll
    
    class TestFailureReportingTest extends Specification {
    
      def cleanup() {
        specificationContext.currentSpec.listeners
          .findAll { it instanceof MyGlobalExtension.ErrorListener }
          .each {
            def errorInfo = (it as MyGlobalExtension.ErrorListener).errorInfo
            if (errorInfo)
              println "Test failure in feature '${specificationContext.currentFeature.name}', " +
                "exception class ${errorInfo.exception.class.simpleName}"
            else
              println "Test passed in feature '${specificationContext.currentFeature.name}'"
          }
      }
    
      def normalFeature() {
        expect:
        0 == 1
      }
    
      def parametrisedFeature() {
        expect:
        a == b
    
        where:
        a | b
        2 | 3
        4 | 5
        6 | 6
      }
    
      @Unroll
      def unrolledParametrisedFeature() {
        expect:
        a == b
    
        where:
        a | b
        6 | 7
        8 | 9
        0 | 0
      }
    }
    

    备注:错误信息是 在一个 cleanup: 功能方法中的块,因为扩展只在包括该块在内的整个功能/迭代完成运行后启动。所以你真的要用 清除() 方法,但您无论如何都需要它,并且它避免了代码重复。

    p.p.s.:当然,您也可以从方法拦截器中进行泛型日志记录,并跳过整个 清除() 方法。但是,您不再能够使日志输出测试特定,它将用于 全部的 你的测试,而不仅仅是你选择的测试——当然,除非你硬编码一个包或规范名过滤器到拦截器中,或者确保拦截器在spock启动时读取相应的配置文件。