In a Rails project (rails 5.2.2, ruby 2.4.1) I have 2 resources defined, one named "groups" and one named "products". Product objects have a belongs_to relationship with a Group. I wanted to created an rspec shared example group which can test both resources, but I'm having some trouble with the "create" and "update" actions for "products".
I wanted to setup a shared set of example groups which take a hash used to create a new record as a parameter. The example group could then be called within both the "groups_spec.rb" and the "products_spec.rb". I have fixtures for both "groups" and "products. The following is a code example for the "requests/products_spec.rb" which calls the shared examples:
RSpec.describe "Products", type: :request do
fixtures :groups, :products
it_should_behave_like("modify data checks",
Rails.application.routes.url_helpers.api_products_path,
Product,
{ product: {
name: "New Product",
description: "Test product to add or modify",
group_id: Group.first.id,
label: "NP"
} })
end
end
The problem with products is that the new product data requires a group_id which must be valid inside the context of the example group, but I have to be able to retrieve the group_id from outside the example group in order to pass it in.
I'm guessing the real answer is to reorganize how the example group is structured, so I'll take a recommendation on how to restructure the shared example group. Of course, if I'm simply just doing something wrong here, I'll take that answer too.
I use a let
inside of the it_behaves_like
block like below:
RSpec.describe "Products", type: :request do
fixtures :groups, :products
# This shared example probably lives in another file, which is fine
# I don't usually pass in args, instead using everything via `let`
shared_example 'modify_data_checks' do
# you have access to all your variables via let inside here
before do
visit(path)
end
expect(model).to be_a(Product)
expect(group_id).to eq(1)
end
it_behaves_like 'modify_data_checks' do
let(:path) { Rails.application.routes.url_helpers.api_products_path }
let(:model) { Product }
let(:group_id) { Group.first.id }
let(:params) {
product: {
name: "New Product",
description: "Test product to add or modify",
group_id: group_id,
label: "NP"
}
}
end
end
You should be able to pass data around relatively cleanly like this. We're using a pattern similar to this to test polymorphic relationships using shared examples.