This module gets included in a form object within rails.
What is the right way to test it using rspec?
1) Do I test it directly on each model that includes it?
or
2) Do I test the delegation method directly? (i would prefer direct if possible)
If I test it directly, how? I tried and get the below error...
module Registration
class Base
module ActAsDelegation
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def form_fields_mapping
[
{name: :first, model: :user},
{name: :address, model: :address}
]
end
def fields_of_model(model)
form_fields_mapping.select {|record| record[:model] == model }.map {|record| record[:name] }
end
def delegate_fields_to(*models)
models.each do |model|
fields_of_model(model).each do |attr|
delegate attr.to_sym, "#{attr}=".to_sym, to: model if attr.present?
end
end
end
end
end
end
end
module Registration
class Base
include ActiveModel::Model
include ActAsDelegation
def initialize(user=nil, attributes={})
error_msg = "Can only initiate inherited Classes of Base, not Base Directly"
raise ArgumentError, error_msg if self.class == Registration::Base
@user = user
setup_custom_accessors
unless attributes.nil?
(self.class.model_fields & attributes.keys.map(&:to_sym)).each do |field|
public_send("#{field}=".to_sym, attributes[field])
end
end
validate!
end
end
end
require "rails_helper"
RSpec.describe Registration::Base::ActAsDelegation, type: :model do
describe "Class Methods" do
context "#delegate_fields_to" do
let(:user) {spy('user')}
let(:address) {spy('address')}
let(:delegation_fields) { [
{name: :first, model: :user},
{name: :address, model: :address}
]}
it "should delegate" do
allow(subject).to receive(:form_fields_mapping) { delegation_fields }
Registration::Base::ActAsDelegation.delegate_fields_to(:user,:address)
expect(user).to have_received(:first)
expect(address).to have_received(:address)
end
end
end
end
Failure/Error: Registration::Base::ActAsDelegation.delegate_fields_to(:user,:address)
NoMethodError: undefined method `delegate_fields_to' for Registration::Base::ActAsDelegation:Module Did you mean? delegate_missing_to
(I have other code issues in this example, but below resoved the main issue)
As your module is designed to be included, just include it in an empty class in tests. I prepared a simplified example which I verified to work:
module ToBeIncluded
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def a_class_method
:class
end
end
end
class TestSubject
include ToBeIncluded
end
require 'rspec/core'
RSpec.describe ToBeIncluded do
subject { TestSubject }
it 'returns correct symbol' do
expect(subject.a_class_method).to eq(:class)
end
end
In your case probably something along those lines should be fine:
require "rails_helper"
RSpec.describe Registration::Base::ActAsDelegation, type: :model do
class TestClass
include Registration::Base::ActAsDelegation
end
describe "Class Methods" do
context "#delegate_fields_to" do
let(:user) {spy('user')}
let(:address) {spy('address')}
let(:delegation_fields) { [
{name: :first, model: :user},
{name: :address, model: :address}
]}
it "should delegate" do
allow(TestClass).to receive(:form_fields_mapping) { delegation_fields }
TestClass.delegate_fields_to(:user,:address)
expect(user).to have_received(:first)
expect(address).to have_received(:address)
end
end
end
end
Also, you could make anonymous class if you are afraid of name clashes.