Welcome to Working With Rails


Discussion Forums

Discuss all things Ruby on Rails with perhaps the web's most vibrant group of Ruby on Rails enthusiasts.
Newbie - Unit Test & Referential Integrity
6 Posts
Newbie - Unit Test & Referential Integrity

I'm trying to unit test the relationship between two models. First model, contains a relation to the second model but is not required. Therefore, I have a belongs_to in model 2 and a has_one is model 1. In my migration, I created a foreign key constraint between the two, because I don't want to be able to delete model 2 if it has been assigned to model 1. In the database the constraint is working fine; however, the unit test is allowing model 2 to be deleted, after it has already been assigned to model 1.

I've read that Rails will "turn off" RI in test mode, but does that mean it doesn't execute any foreign key constraints in the migration, or is there a setting that I can just "turn on" RI for my testing?


If I understand correctly, you have two models, Model1 and Model2. Model2 belongs to Model1, so it has a model1_id column. Your constraint asserts that the model1_id column on the model2s table cannot be null.

If I've understood this correctly, then deleting a row from the model2s table shouldn't violate the constraint. The foreign key is on the model2s table; delete a model2 row and the foreign key is gone, nothing left to constrain.

If I misunderstood your description, then you could try

ActiveRecord::Base.connection.execute('SET foreign_key_checks = 1')

in your test setup (and appropriate reversal in your teardown). In addition, you might consider preferring to use ActiveRecord validations to manage your association requirements. In this case, you'd likely want to use validates_presence_of in some way.

k...i wrote this late at night while watching Family Guy.

Let me rephrase 2 models - (Model1 & Model2) Model1 only serves as a lookup table Model2 contains a reference to Model1 (ie., Model2.Model1_Id). This relationship is not required; however, if a user associates an item from the lookup Model1 in Model2, then they should not be able to delete that item in Model1.

With those biz rules, I'm not doing any ActiveRecord validations because nulls are allow. Therefore, I'm relying on the Postgres database to maintain the data integrity. I created the constraint in the migration for Model2. In SQL statements on the dev database, Postgres is handling everything correctly. The problem is when I run rake db:test:prepare. Looks like the constraints don't port over the test database and of course the model1.destroy method I call in my unit test passes with flying colors.

I'm a newbie, so not sure if this all makes sense.


I'm figuring out my problem. I had my belongs_to and has_one in the wrong classes.

Anyway, I think I'm going to need to use the example that you mentioned.

ActiveRecord::Base.connection.execute('SET foreign_key_checks = 1')

Where would this typically go in a Rails app?

I'm not convinced that foreign_key_checks is the problem here. Rails turns foreign key checking off for fixture loading, but I believe it's otherwise on in tests. I'd suggest you check how it's set by printing out the value in a test:

def test_foo
  p ActiveRecord::Base.connection.select_value("SELECT @@FOREIGN_KEY_CHECKS")

If that returns 0 or false then I'm wrong and you do need to turn it back on. You could do this in the #setup and #teardown methods for your test class.

def setup
  @old_foreign_key_checks = ActiveRecord::Base.connection.select_value("SELECT @@FOREIGN_KEY_CHECKS")
  ActiveRecord::Base.connection.update("SET FOREIGN_KEY_CHECKS = 1")

def teardown
  ActiveRecord::Base.connection.update("SET FOREIGN_KEY_CHECKS = #{@old_foreign_key_checks}")

k...after a little bit more digging, I'm finding out that when I run, rake db:test:prepare, the constraints are not coming over. They are created fine in my dev environment.

I'm using Postgres...so does db:test:prepare bring over the constraints?

6 Posts
Login to add your message