RSpec与共享示例上下文的可重用方法

时间:2014-07-21 18:31:08

标签: ruby-on-rails ruby testing rspec

我希望在许多RSpec共享示例中包含一个方法来覆盖默认上下文方法:

describe 'Links' do
  describe EditLink do
    it_behaves_like 'update association service', :link do
      def create_association(params, user_symbol)
        AddLink.new(user_symbol).(params)
      end
    end
  end

  describe DeleteLink do
    it_behaves_like 'delete association service', :link do
      def create_association(params, user_symbol)
        AddLink.new(user_symbol).(params)
      end
    end
  end
end

有没有办法提取create_association并在共享示例中传递它,以便它将覆盖共享上下文中的默认方法,而不创建那些do ... end块并在每个{{1}内复制粘贴此方法}}?

3 个答案:

答案 0 :(得分:2)

您应该可以使用RSpec helper methods执行此操作。

在某处添加带有提取方法的模块,例如。 spec/support/helpers.rb

module Helpers
  def create_association(params, user_symbol)
    AddLink.new(user_symbol).(params)
  end
end

确保rails_helper.rb中需要这样做。通常,使用默认RSpec设置时,spec/support下的任何内容都是必需的。

RSpec需要配置为使用模块扩展共享示例组。这可以在rails_helper.rb配置部分中完成:

RSpec.configure do |config|
  config.extend Helpers
end

然后可以从共享示例块中调用该方法:

it_behaves_like 'delete association service', :link do
  create_association(params, user_symbol)
end

文档解释了将模块限制为特定组的其他选项。

要在正常示例中使用帮助程序(例如,在共享示例之外),您需要include代替extend(或除此之外):

RSpec.configure do |config|
  config.include Helpers
end

答案 1 :(得分:0)

首先,注意:如果这两个示例的create_association覆盖相同,并且它们都在示例代码中的外部上下文中,则不必将其声明两次 - - 只需在外部语境中声明它:

describe 'Links' do
  def create_association(params, user_symbol)
    AddLink.new(user_symbol).(params)
  end

  describe EditLink do
    # ... (etc.)
  end

  describe DeleteLink do
    # ... (etc.)
  end
end

第二:我还没想出如何覆盖共享示例方法(我仍然怀疑有一种方法,在方法调用站点上的一些东西会使它解析为重新定义的版本,但我太新了Ruby已经弄明白了,但我找到了以下解决方法:

RSpec.shared_examples 'an association service' do
  def create_association(params, user_symbol)
    if defined? create_association_override
      create_association_override
    else
      # ...whatever the default value is...
    end
  end
end

在规范中:

describe 'Links' do
  def create_association_override(params, user_symbol)
    AddLink.new(user_symbol).(params)
  end
  # ...(etc.)

伪造虚拟方法查找是一种丑陋的方式,如果我从来没有听说过更好的方法,我会非常惊讶,但它确实有效。

答案 2 :(得分:0)

我不确定你想要完成的是一般的好方法。它闻起来像你需要重新思考和重构你的RSpec例子。您将需要进行一些错误检查以确保 您用于创建关联的所有方法都是有效的,并且您传入的任何参数同样没有错误。这引入了脆弱性。此外,您可能会牺牲可读性,从而增加维护成本。

尽管如此,我创建了示例代码来说明如何完成此操作。这并不复杂。

基本上,你想完成两件事:

  1. 具有由某些默认方法
  2. 创建的默认association
  3. 将shared_example的参数设为可选参数 如果没有将任何参数传递给shared_example,它应该只使用默认方法: it_behaves_like 'update association service'

    但是如果你传入参数,那么使用这些参数(方法和参数等)来创建关联:

    it_behaves_like 'update association service', DeleteLink, :new, []

    上面的参数是要实例化的类用于创建关联的方法(发送给类),以及的参数方法。(论证显然取决于你如何编写共享示例。)

  4. 这只是Ruby。在您的shared_example中,只需检查参数是否为nil,然后相应地执行操作。这是基本的想法:

    RSpec.shared_examples 'update association service' do |optional_class, optional_method, parameters|
    
      let(:assoc) {
        if optional_class.nil? || optional_method.nil?
          default_assoc  # defined prior to this (needs error checking) 
                         #  See the gist for a full example
        else
          optional_class.send(optional_method, *parameters)
        end
      }
    
      it 'does something with the assoc' do
        expect(assoc.behavior_tested).to eq expected_behavior
      end
    
    end
    

    `

    以下是您在Rspec示例组中使用它的方法:

    RSpec.describe 'Links' do
    
      # set the default method. In the gist, I use a shared_context to do this
      let(:default_assoc) {
          default_class.send(default_method, arg1, arg2)
      }
    
    
      describe 'AddLink' do
    
        # Since no arguments are passed to this shared example,
        # it will use 'default_assoc' in the shared_example.
        it_behaves_like 'update association service'
    
      end
    
      describe 'EditLink' do
    
        # Override how the association is created in the shared_example
        # by passing in arguments that are used to create it:
        it_behaves_like 'update association service', MySpecialAssocClass, :special_create_method, [arg1, arg2, arg3, arg4]
    
      end
    end
    

    我在Github上的一个充分的工作示例:weedySeaDragon - rspec-shared-context-examples-optional.rb