One big difference between them is that Capybara run in a thread so in the main thread your application is running, and in another thread Capybara is executing the find, click and all other Capybara methods.
Getting randomly undefined method `collect!’ for nil:NilClass
My amazing colleague Andrea spends almost the day in order to understand what happen and where it happens.
This error is raised from the ActiveRecord::Base.find_by_sql method.
When this method is call, sometimes
nil and then the undefined method `collect!’ for nil:NilClass error is raised.
The reason why this
select_all method returns nil sometimes is due to the ActiveRecord::ConnectionAdapters::QueryCache module which is not written thread safe.
Let say you have a Cucumber step which in the end execute some SELECT on a users table and a second one which executes an UPDATE of one user. The first one runs in the main thread while the second one runs in the capybara thread.
This UPDATE will execute the ActiveRecord::ConnectionAdapters::QueryCache callbacks which will executes the
clear_query_cache method which reset the
Now look carefully the
@query_cache.has_key? is called and if it returns
true then line 5 the query result is fetched from the cache using ` @query_cache[sql]`.
This code is not thread safe because in our case the
@query_cache variable is cleared between the call to
@query_cache.has_key? and ` @query_cache[sql]
So the method is returning nil
and this nil
is returned in the end by the connection.select_all` (in the ActiveRecord::Base.find_by_sql method) and the error is raised.
It’s really easy to fix this unsafe code. We don’t really care if the cache has a key or not. We just want to read the value of the key. If the key is not existing then a
nil will be returned so here is the new code:
- removed the check ‘if the key exists’
- read the value from the cache for the key
- in the case the cache returned something we return it otherwise the query is executed and the result is stored in the cache
We created in our project a Rails initializer which monkey patch the
ActiveRecord::ConnectionAdapters::QueryCache.cache_sql with the fix.
It’s now working !
What a happiness Andrea and me felt to see the tests passing again ! It was a nice challenge and we succeed. :-)
Hopefully this could help someone.
Update: I have created a Pull Request in order to fix the issue in Rails directly.