I mentioned yesterday that I was saving juicy topics for the book I’m writing, but this is just one that’s too good to miss from posting here too. Consider it (and the posts before it) a sample. The book is in a very similar vein.
Today we were working on our application at work which we refer to as three-point-oh. In three-point-oh, there were some features that were broken unrelated to the work we were currently doing on the donations
branch which we had just merged into master
. It was one of those issues where you could run the features that were breaking by themselves and they’d work just fine.
You know the type.
In our system we have an ActivityObserver
which creates an Activity
record every time someone performs any kind of CRUD action upon any observed class in our system. The catch is that User.current_user
must be set for an activity record to be created, otherwise there’s no record of CRUD.
One of the observed classes was Contact
. One of our features runs the db:seed
task for its own setup (the very first line of the Background) and in this we set up a Contact
record for the scenarios. Of course by it being the first line, we’re not going to have a currently logged in user, and therefore an Activity
record is not going to be created.
But what happens if we run another scenario where we log in? Well, then User.current_user
will be set to that user whom we log in with. Then, of course, Cucumber will perform its dutiful task once the scenario has finished running and clear the database, thereby eradicating all records, including the one that User.current_user
is set to . What Cucumber does (and should) not do is destroy objects. When we ran the features in a group, if any of those features were logging in they would be setting User.current_user
, then of course that related record would be being wiped when the scenario completed.
Then came the seeding feature. This is the feature that runs the db:seed
task for itself and because User.current_user
was set in a prior ran feature, it was creating Activity
records for users that no longer exist. When the app then went to display these activities on the homepage, it would attempt access the avatar
method on a user that no longer existed, thus giving us an undefined method avatar for nil:NilClass
.
This was because between requests in test mode, classes are not reloaded. Therefore, User.current_user
would not be unset. To unset it, we specify this in a file located at features/support/start.rb:
Before do
User.current_user = nil
end
We could probably also use foreign key constraints to ensure that when we create an activity record that the user record we’re creating it for exists, but that’s information probably best kept for the book or another blog post.