Is there a way to load Rails fixtures in a specific order while running tests? For example, take the following classes...
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, through: :memberships
end
class Group < ActiveRecord::Base
has_many :memberships
has_many :users, through: :memberships
end
class Membership < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
Memberships
have a database level foreign key constraint requiring Users
and Groups
to be present before they can be created. However, because Rails loads fixture alphabetically, Memberships
are loaded before Users
, and an error occurs stating the relationships do not exist (rightly so).
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: insert or update on table "memberships" violates foreign key constraint
Is there a way to load the Users
and Groups
fixtures before loading Memberships
while running tests?
Your problem isn't the order that Rails is running your tests. I had the same issue as you, and after debugging for a few hours realized that ActiveRecord actually DOES disable referential integrity during insertion of fixture records to prevent these types of errors for Postgresql.
The catch is that your test database user must have superuser privileges on the test db for ActiveRecord to successfully disable referential integrity as needed for fixtures.
Here's how you fix your issue:
Perform the following command:
ALTER ROLE yourtestdbuser WITH SUPERUSER;
Enjoy your properly working fixtures.
The next release of Rails will have a warning (which I think is much needed) when a person is running a test database with Postgresql and a user without a superuser role. I found this on a Rails GitHub issue suggesting the warning.
Granting PostgreSQL superuser privileges to your "test" account allows Rails to work the way it wants to work. In cases where this is not desired/feasible...
Yes, it is possible -- if not supported -- to control the order in which fixtures are loaded and deleted (both of which are important).
In test/test_helper.rb
:
class ActiveRecord::FixtureSet
class << self
alias :orig_create_fixtures :create_fixtures
end
def self.create_fixtures f_dir, fs_names, *args
# Delete all fixtures that have foreign keys, in an order that
# doesn't break referential integrity.
Membership.delete_all
reset_cache
# If we're adding any {user, group} fixtures, add them [a] in that
# order, [b] before adding any other fixtures which might have
# references to them.
fs_names = %w(users groups) & fs_names | fs_names
orig_create_fixtures f_dir, fs_names, *args
end
end
Tested with Rails 4.2.3, and only when using fixtures :all
.
For, at least, Rails 5, you can replace fixtures :all
with fixtures %w[users groups memberships]
(in test/test_helper.rb
). It will INSERT them in that order. But do not forget any fixture file :-)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With