Reputation: 1147
I have a Concern defined like this:
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
and a variety of models that use the concern like this:
Class MyThing < ActiveRecord::Base
include Shared::Injectable
...
injectable_attributes [:attr1, :attr2, :attr3, ...]
...
end
This works as intended, and generates a set of new methods that I can call on an instance of the class:
my_thing_instance.injected_attr1
my_thing_instance.injected_attr2
my_thing_instance.injected_attr3
My issue comes when I am trying to test the concern. I want to avoid manually creating the tests for every model that uses the concern, since the generated functions all do the same thing. Instead, I thought I could use rspec's shared_example_for
and write the tests once, and then just run the tests in the necessary models using rspec's it_should_behave_like
. This works nicely, but I am having issues accessing the parameters that I have passed in to the injectable_attributes
function.
Currently, I am doing it like this within the shared spec:
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
This works, but is obviously a horrible way to do this. Is there an easy way to access only the values passed in to the injectable_attributes function, either through an instance of the class or through the class itself, rather than looking at the methods already defined on the class instance?
Upvotes: 0
Views: 100
Reputation: 16793
Since you say that you "want to avoid manually creating the tests for every model that uses the concern, since the generated functions all do the same thing", how about a spec that tests the module in isolation?
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
Then, as an option, if you wanted to write a general spec to test whether an object actually has injectable attributes or not without repeating what you've got in the module spec, you could add something like the following to your MyThing
spec file:
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
Upvotes: 1
Reputation: 103
What about trying something like this:
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
It doesn't create individual test cases for each attribute, but they should all have an assertion for each attribute. This might at least give you something to work from.
Upvotes: 0