ActiveSupport::Concern 的动态测试生成
Dynamic generation of tests for an ActiveSupport::Concern
我有一个定义如下的问题:
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
这按预期工作,并生成了一组我可以在 class:
的实例上调用的新方法
my_thing_instance.injected_attr1
my_thing_instance.injected_attr2
my_thing_instance.injected_attr3
当我试图测试问题时,我的问题就来了。我想避免为每个使用关注点的模型手动创建测试,因为生成的函数都做同样的事情。相反,我认为我可以使用 rspec 的 shared_example_for
并编写一次测试,然后只 运行 使用 rspec 的 [=16] 在必要的模型中进行测试=].这很好用,但我在访问传递给 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
这可行,但显然是一种糟糕的方法。有没有一种简单的方法可以仅通过 class 的实例或通过 class 本身访问传递给 injectable_attributes 函数的值,而不是查看已经定义的方法在 class 实例上?
尝试这样的事情怎么样:
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
它不会为每个属性创建单独的测试用例,但它们应该对每个属性都有一个断言。这至少可以给你一些工作的机会。
既然你说你 "want to avoid manually creating the tests for every model that uses the concern, since the generated functions all do the same thing",那么单独测试模块的规范怎么样?
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
然后,作为一个选项,如果您想编写一个通用规范来测试对象是否确实具有可注入属性而不重复模块规范中的内容,您可以添加类似以下内容的内容您的 MyThing
规范文件:
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
我有一个定义如下的问题:
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
这按预期工作,并生成了一组我可以在 class:
的实例上调用的新方法my_thing_instance.injected_attr1
my_thing_instance.injected_attr2
my_thing_instance.injected_attr3
当我试图测试问题时,我的问题就来了。我想避免为每个使用关注点的模型手动创建测试,因为生成的函数都做同样的事情。相反,我认为我可以使用 rspec 的 shared_example_for
并编写一次测试,然后只 运行 使用 rspec 的 [=16] 在必要的模型中进行测试=].这很好用,但我在访问传递给 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
这可行,但显然是一种糟糕的方法。有没有一种简单的方法可以仅通过 class 的实例或通过 class 本身访问传递给 injectable_attributes 函数的值,而不是查看已经定义的方法在 class 实例上?
尝试这样的事情怎么样:
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
它不会为每个属性创建单独的测试用例,但它们应该对每个属性都有一个断言。这至少可以给你一些工作的机会。
既然你说你 "want to avoid manually creating the tests for every model that uses the concern, since the generated functions all do the same thing",那么单独测试模块的规范怎么样?
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
然后,作为一个选项,如果您想编写一个通用规范来测试对象是否确实具有可注入属性而不重复模块规范中的内容,您可以添加类似以下内容的内容您的 MyThing
规范文件:
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