You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何创建可展示通过测试expect详情的自定义RSpec Formatter?

解决方案:扩展RSpec Reporter以生成完整CouchDB测试结果

你已经搞定了失败测试的上报逻辑,现在只需要补全通过测试的元数据收集,就能生成符合要求的JSON结构并推送到CouchDB。下面是一步步的落地方案:

1. 准备必要依赖

先确保你的项目里有这些工具(可以加到Gemfile):

  • securerandom:生成唯一的_id字段
  • json:处理JSON序列化
  • rest-client:发送请求到CouchDB(用你熟悉的HTTP库也可以)
  • 若要获取Git仓库SHA,确保环境能执行git命令

2. 完整的EliteReporter实现

我们需要在Reporter里跟踪全局测试时间、每个测试步骤的细节,还要从RSpec的example对象和元数据中提取更多关键信息。以下是完整代码:

require 'rspec/core/formatters/base_formatter'
require 'securerandom'
require 'json'
require 'rest_client'

class EliteReporter < RSpec::Core::Formatters::BaseFormatter
  RSpec::Core::Formatters.register self, :start, :example_passed, :example_failed, :stop

  def initialize(output)
    super(output)
    @test_steps = []
    @global_start_time = nil
    # 替换成你的CouchDB地址和数据库名
    @couchdb_endpoint = "http://your-couchdb-host:5984/your-test-results-db"
  end

  # 全局测试启动时记录起始时间戳
  def start(_start_notification)
    @global_start_time = Time.now.utc.to_i * 1000 # 转为毫秒级时间戳,匹配示例格式
  end

  # 处理通过的测试用例
  def example_passed(passed_notification)
    example = passed_notification.example
    # 把单个测试步骤加入列表
    @test_steps << {
      result: "pass",
      description: example.description
      # 如果需要单步骤执行时间,可加:run_time: example.metadata[:execution_result].run_time
    }
  end

  # 复用你已实现的失败测试逻辑(这里保留基础结构)
  def example_failed(failed_notification)
    example = failed_notification.example
    @test_steps << {
      result: "fail",
      description: "#{example.description} - 错误信息:#{failed_notification.exception.message}"
    }
  end

  # 全局测试结束时生成完整JSON并推送CouchDB
  def stop(stop_notification)
    global_end_time = Time.now.utc.to_i * 1000
    # 获取当前Git仓库的SHA1(如果环境无git,可改用环境变量传入)
    sha1_repo = `git rev-parse HEAD`.strip rescue "unknown"
    # 确定全局测试结果
    global_result = @test_steps.all? { |s| s[:result] == "pass" } ? "pass" : "fail"

    # 从顶层测试组提取用例元数据(假设一个RSpec Group对应一个测试用例)
    top_group = stop_notification.notification.group
    testcase_id = top_group.metadata[:testcase_id] || "TC_unknown"
    testcase_title = top_group.metadata[:testcase_title] || top_group.description

    # 构建测试层级路径
    hierarchy = []
    current_group = top_group
    while current_group
      hierarchy.unshift(current_group.description)
      current_group = current_group.parent_group
    end

    # 组装目标JSON结构
    final_result = {
      _id: SecureRandom.uuid, # 生成唯一ID
      sha1_repo: sha1_repo,
      steps: @test_steps,
      time_start: @global_start_time.to_s,
      time_end: global_end_time.to_s,
      result: global_result,
      testcase_title: testcase_title,
      testcase_id: testcase_id,
      hierarchy: hierarchy
    }

    # 推送到CouchDB
    begin
      response = RestClient.post(
        @couchdb_endpoint,
        final_result.to_json,
        content_type: :json
      )
      @output.puts "✅ 测试结果已成功推送至CouchDB: #{response.body}"
    rescue RestClient::Exception => e
      @output.puts "❌ 推送CouchDB失败: #{e.message}"
    end
  end
end

3. 关键细节说明

  • 自定义元数据提取:你可以在RSpec测试组里通过tag定义testcase_id和testcase_title,比如:

    RSpec.describe "Komfort_TSG_HBFS_03_to_Komfort2_TSG_HBFS_03", 
      testcase_id: "TC_1zu1_BAF_Komfort_TSG_HBFS_03_to_Komfort2_TSG_HBFS_03",
      testcase_title: "Komfort_TSG_HBFS_03_to_Komfort2_TSG_HBFS_03" do
      
      it "Time for Routing expect OK: 126 micro seconds (DLC and Data also OK)" do
        # 测试逻辑
      end
    end
    

    这样Reporter就能直接从metadata里拿到这些字段,完全匹配你需要的格式。

  • 层级路径构建:通过遍历测试组的父级,从底层到顶层收集组描述,再反转得到正确的层级顺序(比如示例里的["Hardware Integration Test", "1 - Routing", ...])。

  • CouchDB交互:首次提交时不需要提供_rev,CouchDB会自动生成并返回这个字段,后续更新时才需要携带。

4. 如何使用这个Reporter

运行RSpec时指定该格式化器即可:

rspec --format EliteReporter --out /dev/null

(--out /dev/null是为了屏蔽默认输出,如果你需要保留默认日志,可以同时指定多个格式化器,比如--format progress --format EliteReporter --out couchdb.log)

内容的提问来源于stack exchange,提问作者Attila Strba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:20:33