Using Thinking Sphinx with Cucumber
While I highly recommend you stub out your search requests in controller unit tests/specs, I also recommend you give your full stack a work-out when running search scenarios in Cucumber.
This has gotten a whole lot easier with the ThinkingSphinx::Test class
and the integrated Cucumber support, but it’s still not perfect, mainly
because generally everyone (correctly) keeps their database changes
within a transaction. Sphinx talks to your database outside Rails’
context, and so can’t see anything, unless you turn these transactions
off.
It’s not hard to turn transactions off in your features/support/env.rb
file:
Cucumber::Rails::World.use_transactional_fixtures = false
But this makes Cucumber tests far more fragile, because either each scenario can’t conflict with each other, or the database needs to be cleaned before and after each scenario is run.
Pretty soon after I added the inital documentation for this, a few expert Cucumber users pointed out that you can flag certain feature files to be run without transactional fixtures, and the rest use the default:
@no-txn
Feature: Searching
In order to find things as easily as possible
As a user
I want to search across all data on the site
This is a good step in the right direction, but it’s not perfect - you’ll still need to clean up the database. Writing steps to do that is easy enough:
Given /^a clean slate$/ do
Object.subclasses_of(ActiveRecord::Base).each do |model|
next unless model.table_exists?
model.connection.execute "TRUNCATE TABLE `#{model.table_name}`"
end
end
(You can also use Database Cleaner, as noted by Thilo in the comments).
But adding that to the start and end of every single scenario isn’t particularly DRY.
Thankfully, there’s Before and After hooks in Cucumber, and they can
be limited to scenarios marked with certain tags. Now we’re getting
somewhere!
Before('@no-txn') do
Given 'a clean slate'
end
After('@no-txn') do
Given 'a clean slate'
end
And here’s a bonus step, to make indexing data a little easier:
Given /^the (\w+) indexes are processed$/ do |model|
model = model.titleize.gsub(/\s/, '').constantize
ThinkingSphinx::Test.index *model.sphinx_index_names
end
So, how do things look now? Well, you can write your features normally -
just flag them with no-txn, and your database will be cleaned up both
before and after each scenario.
My current preferred approach is adding a file named
features/support/sphinx.rb, containing this code:
require 'cucumber/thinking_sphinx/external_world'
Cucumber::ThinkingSphinx::ExternalWorld.new
Before('@no-txn') do
Given 'a clean slate'
end
After('@no-txn') do
Given 'a clean slate'
end
And I put the step definitions in either
features/step_definitions/common_steps.rb or
features/step_definitions/search_steps.rb.
So, now you have no excuse to not use Thinking Sphinx with your Cucumber suite. Get testing!
Hi Pat,
nice write up.
The recent version of cucumber uses database_cleaner. So that you just need to add these steps.
Before(‘@no-txn’) do
DatabaseCleaner.start
end
After(‘@no-txn’) do
DatabaseCleaner.clean
end
Thanks Thilo - couldn’t find it mentioned in the cucumber source (0.4.4 or 0.6.3), but it’s definitely an option if people want to install that gem. Have updated the blog post to mention it.
hey pat!
using a setup very similar to the one you described above, i get intermittent failures on search. is this something you have experienced as well?
i recall from your testing sphinx page that you were sleeping a quarter of a second after re-indexing the models you were testing. that is left that out of your Given /^the (\w+) indexes are processed$/ step definition. should i still need that?
Hi Neal
You will still need the sleep line - not sure why I left that out of this blog post (I’ll amend it with a note at some point soon). If you’ve got an older/slower machine, then you may need to increase the sleep length, too.
When you say you get failures - do you mean the search results aren’t there? (Sleeping so searchd can catch up should help that) Or do you mean errors are being raised?
hey pat! thanks for getting back.
when i say that i get failures, i do mean that the search results aren’t there. but it only happens sometimes, say, 1 out of 10 times i run the test. i guess my machine is pretty fast … or something? i’ll sleep for 0.1 seconds and see if that works out.
thanks again!
-n