首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >RSpec 2和3如何创建一个将生成大量示例的助手?

RSpec 2和3如何创建一个将生成大量示例的助手?
EN

Stack Overflow用户
提问于 2014-08-07 20:59:55
回答 1查看 540关注 0票数 0

我试图创建帮助器,以统一和良好的方式测试JSON响应。

对于更多描述性和特定的故障,我希望每个JSON原子生成一个示例。

示例语法:

代码语言:javascript
运行
复制
RSpec.describe "some JSON API View" do

  setup_objects

  before { render }

  describe "response" do

    subject { rendered }

    it_conforms_to_json(
      id: 27,
      email: "john.smith@example.com",
      name: "John",
      profile_description: %r{professional specialist},
      nested: {
        id: 37,
        status: "rejected"
      }
    )

  end

end

因此,这个片段将等价于:

代码语言:javascript
运行
复制
RSpec.describe "some JSON API View" do

  setup_objects

  before { render }

  describe "response" do

    subject { rendered }

    it 'has equal value at object["id"]' do
      expect(subject["id"]).to eq(27)
    end

    it 'has equal value at object["email"]' do
      expect(subject["email"]).to eq("john.smith@example.com")
    end

    it 'has equal value at object["name"]' do
      expect(subject["name"]).to eq("John")
    end

    it 'has matching value at object["profile_description"]' do
      expect(subject["profile_description"]).to match(%r{professional specialist})
    end

    it 'has equal value at object["nested"]["id"]' do
      expect(subject["nested"]["id"]).to eq(37)
    end

    it 'has equal value at object["nested"]["status"]' do
      expect(subject["nested"]["status"]).to eq("rejected")
    end

  end

end

通过这段代码,我可以轻松地做到这一点:

代码语言:javascript
运行
复制
module JsonHelper
  extend self

  def it_conforms_to_json(json)
    generate_examples_for(json)
  end

  private

  def generate_examples_for(json, opts)
    with_prefix = opts.fetch(:with_prefix, [])

    if json.is_a?(Hash)

      json.each do |key, new_json|
        new_prefix = with_prefix + [key.to_s]
        generate_examples_for(new_json, opts.merge(with_prefix: new_prefix))
      end

    elsif json.is_a?(Array)

      raise NotImplemented.new("Arrays are not allowed yet")

    elsif json.is_a?(String) || json.is_a?(Numeric)

      it "is expected to have equal value at json[\"#{with_prefix.join('"]["')}\"]" do
        value = JSON.parse(subject)
        with_prefix.each { |key| value = value[key.to_s] }
        expect(value).to eq(json)
      end

    end
  end
end

只需通过扩展它来启用它:rspec_config.extend JsonHelper

当你想到相当失败的信息时,明显的问题就开始了:

  • 它们显示回溯,包括it "is expected...bla-bla-bla的位置
  • 它们不包括it_conforms_to_json(...)呼叫的位置。

首先,通过添加回退跟踪排除/清除模式是可以修复的,但是它会导致一个完整的回溯跟踪,因为所有内容都是经过过滤的。

第二个和以前的版本可以通过在expect中包装begin..rescue语句来修复,通过使用file_path:line of it_conforms_to_json(...)调用预先加上file_path:lineofit_conforms_to_json(...)调用和重新引发修改过的异常来用回溯来破坏。

在前两个问题得到解决后,出现了新的问题:

  • “无法从回溯中找到匹配线”

怀疑的方法是find_first_non_rspec_line (或类似的方法),它通过应用默认的rspec排除regex‘’es在it调用的回溯跟踪(而不是异常回溯)中找到第一行。

它可以通过损坏RSpec内部部件来修复,即:

  • 对于rspec 2:应用此regex‘’es的猴子补丁方法
  • 对于rspec 3:取消常量LIB_REGEX并再次定义它,将此助手文件添加到此正则表达式中
  • 对于rspec 3(最近的小版本/补丁版本):相同,但使用IGNORE_REGEX

这个补丁的代码变得糟糕和不可读,它将是地狱的维护,因为它依赖于rspec内部实现,这可以改变从小/补丁版本到版本。谁有兴趣读这个丑陋的东西这里

更多的是,对于rspec 2和rspec 3,它的工作方式不同。

rspec 2,完美的要求:

代码语言:javascript
运行
复制
    Failures:

    1) A json response is expected to have equal value at json["id"]
       Failure/Error: it_conforms_to_json(

         expected: 37
              got: 25

         (compared using ==)
       # ./spec/simple_with_fail_spec.rb:10:in `block in <top (required)>'

    2) A json response is expected to have equal value at json["name"]
       Failure/Error: it_conforms_to_json(

         expected: "Smith"
              got: "John"

         (compared using ==)
       # ./spec/simple_with_fail_spec.rb:10:in `block in <top (required)>'

rspec 3,略为偏离,但仍可接受:

失败:

代码语言:javascript
运行
复制
    1) A json response is expected to have equal value at json["id"]
       Failure/Error: expect(value).to eq(json)

         expected: 37
              got: 25

         (compared using ==)
       # ./spec/simple_with_fail_spec.rb:10:in `block in <top (required)>'
       # ./lib/rspec/json_expectations/expectations.rb:32:in `block in generate_examples_for'

    2) A json response is expected to have equal value at json["name"]
       Failure/Error: expect(value).to eq(json)

         expected: "Smith"
              got: "John"

         (compared using ==)
       # ./spec/simple_with_fail_spec.rb:10:in `block in <top (required)>'
       # ./lib/rspec/json_expectations/expectations.rb:32:in `block in generate_examples_for'

以下是一些问题:

  • 有什么内置的公共API来实现它吗?
  • 这里的命名似乎有问题,不是期望,而是什么.?
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2014-08-18 20:31:37

因此,它现在有点难看(虽然是第一次迭代),但是很有效。不使用RSpec内部组件,只使用公共RSpec API,不使用很好的错误消息,甚至无需清除回溯跟踪:

代码语言:javascript
运行
复制
module RSpec
  module JsonExpectations
    class JsonTraverser
      def self.traverse(errors, expected, actual, prefix=[])
        if expected.is_a?(Hash)

          expected.map do |key, value|
            new_prefix = prefix + [key]
            if actual.has_key?("#{key}")
              traverse(errors, value, actual["#{key}"], new_prefix)
            else
              errors[new_prefix.join("/")] = :no_key
              false
            end
          end.all?

        elsif expected.is_a?(String) || expected.is_a?(Numeric)

          if actual == expected
            true
          else
            errors[prefix.join("/")] = {
              actual: actual,
              expected: expected
            }
            false
          end

        else
          raise NotImplementedError, "#{expected} expectation is not supported"
        end
      end
    end
  end
end

RSpec::Matchers.define :include_json do |expected|
  match do |actual|
    unless expected.is_a?(Hash)
      raise ArgumentError, "Expected value must be a json for include_json matcher"
    end

    RSpec::JsonExpectations::JsonTraverser.traverse(
      @include_json_errors = {},
      expected,
      JSON.parse(actual)
    )
  end

  # RSpec 2 vs 3
  send(respond_to?(:failure_message) ?
       :failure_message :
       :failure_message_for_should) do |actual|
         res = []

         @include_json_errors.each do |json_path, error|
           res << %{
           json atom on path "#{json_path}" is missing
           } if error == :no_key

           res << %{
           json atom on path "#{json_path}" is not equal to expected value:

             expected: #{error[:expected].inspect}
                  got: #{error[:actual].inspect}
           } if error.is_a?(Hash) && error.has_key?(:expected)
         end

         res.join("")
       end

end

它现在允许这样的语法:

代码语言:javascript
运行
复制
        it "has basic info about user" do
          expect(subject).to include_json(
            id: 37,
            email: "john.smith@example.com",
            name: "Smith"
          )
        end

它会生成这样的错误:

代码语言:javascript
运行
复制
  F

  Failures:

    1) A json response has basic info about user
       Failure/Error: expect(subject).to include_json(

                    json atom on path "id" is not equal to expected value:

                      expected: 37
                           got: 25

                    json atom on path "name" is not equal to expected value:

                      expected: "Smith"
                           got: "John"

       # ./spec/simple_with_fail_spec.rb:11:in `block (2 levels) in <top (required)>'

  Finished in 0.00065 seconds
  1 example, 1 failure

  Failed examples:

  rspec ./spec/simple_with_fail_spec.rb:10 # A json response has basic info about user

同时适用于RSpec 2和3。

如果有人感兴趣,就在这里,期望值

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/25192075

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档