Have problems testing methods that query ActiveRecord?
Sometimes you’ll run into a problem that won’t reveal an answer. I ran into a problem I had come across before in that past to which I knew I hadn’t really solved. I have a custom method that looks up an ActiveRecord query and makes a change. But both times I’ve tried to test the method I would always end up with the test showing that no changes had occurred. What bothered me is that the code worked fine in the rails console and in production. So I spent a lot of time, both times, trying to figure it out.
So I asked Chris about it and he looked at my gist of what was going on. Walking through each item it did appear to be a somewhat elusive problem. During this process I was using pry again to test the change my method was doing to the user object. (side note: pry worked in the console but not as expected within the test-suite/guard) In doing so I found that the change worked within the scope of the method I was testing, but as soon as I returned out of the method the test itself was checking an unchanged object.
The test that failed was much like:
main_user = User.where(email: "[email protected]").first main_user.update(confirmed_at: nil) main_user.confirmed_at.must_be_nil CustomMethod.confirmed_now("[email protected]") main_user.confirmed_at.wont_be_nil
The error said that the item was still nil. Even though pry showed that the change happened within the method. Because pry showed that the user object was changing (at least within the scope of the method) I went ahead and decided to return the user object from the method. So confirmed_now will now return the user object. The test now looked like:
main_user = User.where(email: "[email protected]").first main_user.update(confirmed_at: nil) main_user.confirmed_at.must_be_nil user = CustomMethod.confirmed_now("[email protected]") user.confirmed_at.wont_be_nil
And that worked. After that Chris pointed out to me about using main_user.reload . I plugged that in:
main_user = User.where(email: "[email protected]").first main_user.update(confirmed_at: nil) main_user.confirmed_at.must_be_nil CustomMethod.confirmed_now("[email protected]") main_user.reload main_user.confirmed_at.wont_be_nil
And everything passed! Sweet! So when you run a method that changes a record in your test suite it’s best to use the reload method on it to ensure the object is synced correctly.
This took a lot of time to figure out and I definitely needed some instruction or I wouldn’t have learned about the reload method. It was a pain to come across and I hope that this will help you in avoiding this issue. You can’t learn everything by being an autodidact (self-taught) so I encourage you to connect with and learn with/from others and everyone benefits.
Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!
-Daniel P. Clark
Image by Xava du via the Creative Commons Attribution-NonCommercial-NoDerivs 2.0 Generic License.