Reputation: 1191
I am writing Rspec test for a Ruby On Rails engine. My engine provide ActiveRecord models with a class method is_a_limesurvey_participant(opts = {})
that extends them with class variables and associations. To test my TestModel extension I need to initialize it multiple times in different and consecutive RSpec contexts providing different options. To do so I need to undefine my TestModel otherwise it will "remember" old settings. I do that calling the following utility method before test inspired by this:
def reset_test_model
Object.send(:remove_const,'TestModel')
load File.join(ENGINE_RAILS_ROOT,'spec','dummy','app','models','test_model.rb')
end
And that's ok.
Here's the problem. My engine has a model (SurveyParticipation) which need to be related to my main app TestModel with an association. I do this in my is_a_limesurvey_participant method this way:
def is_a_limesurvey_participant(opts = {})
# ... class variables definitions
has_many :survey_participations, :class_name => "LimesurveyRails::SurveyParticipation", :foreign_key => "participant_id"
LimesurveyRails::SurveyParticipation.belongs_to :participant, :class_name => self.name, :foreign_key => 'participant_id'
puts (LimesurveyRails::SurveyParticipation.reflect_on_association(:participant).klass.object_id == self.object_id)
end
I expected the puts call to always show me true for any examples each time my TestModel call is_a_limesurvey_participant method because LimesurveyRails::SurveyParticipation.reflect_on_association(:participant).klass should be the current TestModel and so it is if I run my test separately.
But running my tests all together show me that LimesurveyRails::SurveyParticipation.reflect_on_association(:participant).klass references always the same TestModel object, that is, the one which was the current one the first time Rspec ran is_a_limesurvey_participant method.
If I try to retrieve both objects by object_id I can see that they both exist in the ObjectSpace:
(rdb:1) ObjectSpace._id2ref(70222826866840)
TestModel(id: integer, name: string, surname: string, email_address: string, extra_id: string, created_at: datetime, updated_at: datetime)
(rdb:1) ObjectSpace._id2ref(70222827277420)
TestModel(id: integer, name: string, surname: string, email_address: string, extra_id: string, created_at: datetime, updated_at: datetime)
I also tried to do the same reset_test_model trick for LimesurveyRails::SurveyParticipation class but even if it gets replaced, the same doesn't happen for its referenced class in the :participant association
In few words belongs_to, after the first call, retrieve the first/wrong/cancelled/unreferenced TestModel. Apart from this situation what is a good pattern to test different class configurations with Rspec? Of course running the test separately makes all working fine, is this the right pattern?
Upvotes: 0
Views: 370
Reputation: 184
I can not understand why are you use such difficult way for create and destroy objects (via :remove_const and file load). Ruby has a garbage collector so just create objects with TestModel.new.is_a_limesurvey_participant(opts = {different_options}) and do not worry about destroy them.
As for puts and that it should always print true: I think that array of all reflected associations (reflect_on_all_associations) for LimesurveyRails::SurveyParticipation won't always return first element for current object.
LimesurveyRails::SurveyParticipation.reflect_on_all_associations.first.klass.object_id
When you run individual tests, then array of associations (after rails load into memory) has only one element and reflect_on_all_associations.first works fine. But for each next test, the first element won't be actual.
One possible solution, may be, is try to change reflect_on_all_associations to reflect_on_association
reflect_on_all_associations.first.klass.object_id
-->
reflect_on_association(:belongs_to).klass.object_id
Upvotes: 0