自定义 RSpec 格式化程序以显示通过的测试和 except 的结果

Custom RSpec formatter to display passed test and result of except

有没有办法创建一个自定义格式化程序,其中显示已通过的测试详细信息以及例外列表?

这个问题的一些背景知识:我们正在尝试迁移到 RSpec 以进行硬件集成和系统测试。结果应该被推送到 CouchDB。我想要实现的是一个可以生成类似于以下代码段的类似 YAML 输出的记者:

   {
   "_id": "0006b6f0-c1bd-0135-1a98-455c37fe87f1",
   "_rev": "1-9c9786b4b4681ee8493f182d4fc56ef9",
   "sha1_repo": "68bb327b540097c10683830f0d82acbe54a47f03",
   "steps": [
       {
           "result": "pass",
           "description": "Time for Routing expect OK: 126 micro seconds (DLC and Data also OK)"
       },
       {
           "result": "pass",
           "description": "Time for Routing expect OK: 146 micro seconds (DLC and Data also OK)"
       },
       {
           "result": "pass",
           "description": "Time for Routing expect OK: 162 micro seconds (DLC and Data also OK)"
       }
],
 "time_start": "1513119108000",
   "time_end": "1513119108000",
   "result": "pass",
   "testcase_title": "Komfort_TSG_HBFS_03_to_Komfort2_TSG_HBFS_03",
   "testcase_id": "TC_1zu1_BAF_Komfort_TSG_HBFS_03_to_Komfort2_TSG_HBFS_03",
   "hierarchy": [
       "Hardware Integration Test",
       "1 - Routing",
       "1.1 Normal Routing",
       "1zu1_BAF_TestCases",
       "CAN_to_CAN"
   ]
}

通过失败的测试,实现这一目标没有问题,但我们还需要通过测试的结果,以便能够创建长期统计数据。

我可以覆盖 RSPec 传递的事件,但示例对象只提供描述,没有更多信息。

class EliteReporter
  RSpec::Core::Formatters.register self, :example_started, :example_passed, :example_failed, :example_finished
  def example_passed(passed)
    @output.printf "pass \n #{passed.example.description}"
  end
end

提前感谢您的帮助。

我想你可以阅读 Module: RSpec::Core::Formatters

您可能会发现一些有用的东西。

P.S。我用过很多次 Cucumber,曾经想自定义 cucumber formatter 来显示每一步的细节,不管是失败还是通过。我终于通过阅读 cucumber 核心找到了解决方案 documents.so 我想也许 rspec 核心文档可以帮助您找到解决方案。

我发现我不能把代码放在注释里,所以我把它放在这里。 按如下方式编辑您的代码:

class EliteReporter
  RSpec::Core::Formatters.register self, :example_started, :example_passed, :example_failed, :example_finished
  def example_passed(example)
      example_failed(example)
  end
end

希望对您有所帮助:)

终于在我同事的帮助下,感谢 Tip from RSPec Emailing list 我可以做到这一点。

我创建了一个收集测试结果的记录器 class,而不是覆盖 Expect 方法。这样在自定义格式化程序中我可以收集所有传递的结果:

 class ExpectWrapper
  def initialize(_expect, _recorder, _description)
    @expect = _expect
    @recorder = _recorder
    @description = _description
  end

  def to(matcher, failure_message=nil)
    begin
      expect_ret = @expect.to(matcher, failure_message) # test

      # for tests that aggregate failures
      if expect_ret.instance_of?(TrueClass)
        @recorder.record(matcher.actual, matcher.description, @description)
      else
        @recorder.record_error(matcher.actual, matcher.description, failure_message, @description)
      end
      expect_ret
    rescue RSpec::Expectations::ExpectationNotMetError => e
      # for test that do not aggregate failures
      @recorder.record_error(matcher.actual, matcher.description, failure_message, @description)
      raise e
    end

  end
end

class Recorder
  def self.start
    @@data = []
    return Recorder.new
  end

  def record(expect, data, description)
    @@data << { :pass => true,  :expect => expect, :value => data, :description => description }
    self
  end

  def record_error(expect, data, failure_message, description)
    @@data << { :pass => false, :expect => expect, :value => data, :message => failure_message,  :description => description }
    self
  end

  def self.data
    @@data
  end

  def expect(object, value, description = "")
    return ExpectWrapper.new(object.expect(value), self, description)
  end
end

自定义格式化程序如下所示,这只是一个示例,数据可能会被放入 JSON 并推送到 Couch:

class EliteVerboseFormatter
  RSpec::Core::Formatters.register self, :example_started, :example_passed, :example_failed, :example_finished


  def initialize(output)
    @output = output
  end

  def example_passed(notification)
    @output.puts( format_output(notification.example, Recorder) )
  end

  def get_test_name( group, description)
    "#{group.example.example_group}/#{description}".gsub('RSpec::ExampleGroups::','')
  end

  def format_output( example, recorder )
    test_case = get_test_name( example.example_group, example.description)
    str = "**********TEST: #{test_case} ************\n"
    recorder.data.each do |d|
       str += sprintf("%s: ---> expected '%-10s' to '%-20s' DESC: %s \n",  d[:pass] ? 'PASS' : 'FAIL',  d[:expect], d[:value], d[:description])
    end
    str

  end

  def example_failed(notification)
    @output.puts(format_output( notification.example, Recorder))

    exception = notification.exception
    message_lines = notification.fully_formatted_lines(nil, RSpec::Core::Notifications::NullColorizer)
    exception_details = if exception
                          {
                              # drop 2 removes the description (regardless of newlines) and leading blank line
                              :message => message_lines.drop(2).join("\n"),
                              :backtrace => notification.formatted_backtrace.join("\n"),
                          }
                        end

     @output.puts RSpec::Core::Formatters::ConsoleCodes.wrap(exception_details[:message], :failure)

  end


end