Jo.P
Jo.P

Reputation: 1159

Database Record not saving/persisting within a Rails test function?

I have an update-type method that I am trying to test in Rails using MiniTest and FactoryGirl. My problem is that although I can see that the update is happening correctly within the update function, it doesn't seem to carry over back into the test-function properly.

These is the objects we are working with, (obj is given a default location to start off with:

location1 = create :location
location2 = create :location
obj = create :object, location: location1

And then we call the update function, which takes id's:

obj.update_location(obj.id, location2.id)

The update function:

def update_location(obj_id, loc_id)
    @obj = Object.find(obj_id)
    @obj.location_id = loc_id
    @obj.save
end

But when, back in the test file, I try to assert the change…

assert_equal obj.location_id, location2.id

...I get a failure. The console tells me that obj.location_id still equals location1.id! Why is this?

It seems that the @obj.save is working properly because I inserted puts @obj.inspect in the update-function and it outputs the correctly updated location_id.

I don't think it has to do with transaction-rollbacks because this is all taking place within a single test. (And I am under the impression that transaction rollbacks only happen in between tests).

In summary, my question is: Why doesn't my update persist back into the rails MiniTest test function?

EDIT: Here is the whole test:

test "update_location" do    
    location1 = create :location
    location2 = create :location

    #give the obj a starting `type`
    obj = create :obj, location: location1

    obj.update_location(obj.id, location2.id)   
    assert_equal obj.location_id, location2.id    
end

Upvotes: 3

Views: 3039

Answers (2)

Jo.P
Jo.P

Reputation: 1159

Issue is resolved, and here is my understanding:

The issue was in how rails' ORM work. The original object 'obj' is loaded up with data from the DB when it is created…but later changes to the DB are not automatically stored to obj. I had thought that obj.some_attribute looks at the database for the record obj in order to find the attribute value--but in actuality I think that the attributes are simply loaded into the variable upon its creation and the database is NOT accessed if I call obj.some_attribute at a later point in time.

Normally, when an update is done through the variable itself, Rails knows to update the variable for you. So when you write obj.some_attribute = 5, and then later on write obj.some_attribute, Rails knows the value is supposed to be 5. But that Rails magic doesn't happen in this situation because it's not updating the record through the variable.

So what needs to be done is simply to reload the object from the database.

So assert_equal obj.reload.location_id, location2.id works!

Upvotes: 6

Josh
Josh

Reputation: 8586

2 questions that might help you find an answer:

  1. Why are you using an instance variable @obj instead of a local variable obj

  2. Why do you have a special method just to update 1 column. Would the update_column not work for you?

Upvotes: 0

Related Questions