【问题标题】:Dynamic generation of tests for an ActiveSupport::Concern为 ActiveSupport::Concern 动态生成测试
【发布时间】:2016-03-11 20:49:14
【问题描述】:

我有一个这样定义的关注点:

module Shared::Injectable 
  extend ActiveSupport::Concern

  module ClassMethods
    def injectable_attributes(attributes)
      attributes.each do |atr|
        define_method "injected_#{atr}" do
           ...
        end
      end
    end
  end

以及像这样使用关注点的各种模型:

Class MyThing < ActiveRecord::Base
  include Shared::Injectable
  ...
  injectable_attributes [:attr1, :attr2, :attr3, ...]
  ...
end

这按预期工作,并生成一组我可以在类的实例上调用的新方法:

my_thing_instance.injected_attr1
my_thing_instance.injected_attr2
my_thing_instance.injected_attr3

当我尝试测试问题时,我的问题就出现了。我想避免为每个使用关注点的模型手动创建测试,因为生成的函数都做同样的事情。相反,我认为我可以使用 rspec 的 shared_example_for 并编写一次测试,然后使用 rspec 的 it_should_behave_like 在必要的模型中运行测试。这很好用,但我在访问已传递给 injectable_attributes 函数的参数时遇到问题。

目前,我在共享规范中这样做:

shared_examples_for "injectable" do |item|
  ...
  describe "some tests" do
    attrs = item.methods.select{|m| m.to_s.include?("injected") and m.to_s.include?("published")}
    attrs.each do |a|
      it "should do something with #{a}" do
        ...
      end
    end
  end
end

这可行,但显然是一种可怕的方法。有没有一种简单的方法可以只访问传递给 injectable_attributes 函数的值,无论是通过类的实例还是通过类本身,而不是查看已经在类实例上定义的方法?

【问题讨论】:

    标签: ruby-on-rails ruby rspec activesupport-concern


    【解决方案1】:

    既然你说你“想要避免为每个使用关注点的模型手动创建测试,因为生成的函数都做同样的事情”,那么单独测试模块的规范怎么样?

    module Shared
      module Injectable
        extend ActiveSupport::Concern
    
        module ClassMethods
          def injectable_attributes(attributes)
            attributes.each do |atr|
              define_method "injected_#{atr}" do
                # method content
              end
            end
          end
        end
      end
    end
    
    RSpec.describe Shared::Injectable do
      let(:injectable) do
        Class.new do
          include Shared::Injectable
    
          injectable_attributes [:foo, :bar]
        end.new
      end
    
      it 'creates an injected_* method for each injectable attribute' do
        expect(injectable).to respond_to(:injected_foo)
        expect(injectable).to respond_to(:injected_bar)
      end
    end
    

    然后,作为一种选择,如果你想编写一个通用规范来测试一个对象是否真的具有可注入属性而不重复你在模块规范中得到的内容,你可以在你的 @ 987654322@规格文件:

    RSpec.describe MyThing do
      let(:my_thing) { MyThing.new }
    
      it 'has injectable attributes' do
        expect(my_thing).to be_kind_of(Shared::Injectable)
      end
    end
    

    【讨论】:

    • 这似乎是最好的方法。当它们都共享相同的功能时,为每个属性创建测试确实感觉是多余的。谢谢!
    【解决方案2】:

    尝试这样的事情怎么样:

    class MyModel < ActiveRecord::Base
      MODEL_ATTRIBUTES = [:attr1, :attr2, :attr3, ...]
    end
    
    it_behaves_like "injectable" do 
      let(:model_attributes) { MyModel::MODEL_ATTRIBUTES }
    end
    
    shared_examples "injectable" do
      it "should validate all model attributes" do 
        model_attributes.each do |attr|
          expect(subject.send("injected_#{attr}".to_sym)).to eq (SOMETHING IT SHOULD EQUAL)
        end
      end
    end
    

    它不会为每个属性创建单独的测试用例,但它们都应该对每个属性都有一个断言。这至少可以为您提供一些工作。

    【讨论】:

      猜你喜欢
      • 2011-09-05
      • 1970-01-01
      • 2012-09-14
      • 2015-12-29
      • 1970-01-01
      • 2012-05-10
      • 2013-02-01
      • 2013-05-26
      • 2014-07-04
      相关资源
      最近更新 更多