I’m starting to add tests to my existing app which uses default_scope for multi-tenancy. The tenant is School
and almost every other object is scoped under current_school
since the app has no public/anonymous access.
I started with a simple association :school
in each factory (User
, Lesson
, Upload
, etc) which was fine for model/unit tests but as soon I started doing integration tests I realized I need to be able to control which School
my tests objects are created under.
I’ve added the following to my spec_helper.rb
:
config.before(:suite) do
School.create!(name: 'Main', subdomain: 'main')
School.create!(name: 'Other', subdomain: 'other')
end
but how do I efficiently use these two ‘preset’ tenants in my factories and/or specs?
I’ve replaced association :school
in every factory with this for now:
school School.where(name: 'Main').first
This is obviously very brittle but at least I can get back to writing tests for a while!
Ordinarily I’m not a fan of using “global” instance variables across all tests using @
, but I think here since so many of your tests rely on it that having a @main_school
and @other_school
that you set up in a before(:all) do end
block would probably be the best way to handle all of these tests. It is sort of a mystery guest, but if the association ends up getting used in 70-80% of your scenarios then it’s probably ubiquitous enough to bend the rules a little.
Thanks @geoffharcourt, that’s really good to know. Will I need to add such a before
block to every _spec.rb
file that references those variables? Or is there somewhere global I can put it?
Please excuse my ignorance - TTN (Total Testing Noob)
If you put it in a block like this in your spec helper it will work everywhere:
RSpec.configure do |config|
config.before(:all) do
# set up your models with @names
end
end
If you include your support directory in spec helper, you can offload this into a separate file, otherwise you can just put the before statement in the big configuration block in spec helper.
@geoffharcourt That’s great, thanks. I’ve got that working.
I’d really like to be able to use these instance variables in my factories, is that possible? Like:
FactoryGirl.define do
factory :teacher do
school_id School.where(name: 'Main').first.id # current_code
association :school, @main_school # is anything like this possible?
name { Faker::Name.first_name }
email { Faker::Internet.safe_email }
end
end
It would make my test code a lot slimmer if I could define @mail_school
as the default association and just override it with @other_school
when I’m testing isolation of data:
create(:teacher, school: @other_school)
I know I can leave the association out of the factory and pass in a school on every create
but since the vast majority will use @main_school
it seems like a lot of repetition.
I don’t think you can access instance variables from RSpec in your factory code, but if it’s possible you probably have to do the work in an after callback, which is explained in the FactoryGirl getting started doc.
Do you need to specify the ID and the association? The first two lines of your factory attributes seem like they are setting up the same association.
Thanks @geoffharcourt, I’ll check out the callback docs.
Sorry, I should’ve made my last post more clear - the line:
school_id School.where(name: 'Main').first.id
is what I’m currently using and I wanted to know if the line:
association :school, @main_school
is possible instead. I guess it isn’t.
Thanks for all your help on this. I feel like I’m actually making some progress with testing now!