views:

1394

answers:

7

I'd like to have some Cucumber/webrat integration tests of my search features that use thinking_sphinx & sphinx but the problem is that the data is loaded and then rolled back in a transaction during a typical cucumber test so there is no way for thinking_sphinx to index it. Alternatively, is there a way to turn transactions off for just a subset of tests?

Have you solved this problem?

[edit - please don't suggesting mocking out the search results. I want the integration test to test the integration of all the features including thinking_sphinx].

A: 

I would not recommend you test a component that you don't own. It will be a pretty brittle test since it would be dependent on the specific ranking algorithm in that specific version of sphinx that could change later and thus return unpredictable results in the future.

if you want to do this I would however suggest running a separate index for testing either using the test database via the mysql adapter or the xmlpipe2 option (xml2pipe obviously lets you change your data set without having to change the indexing options in your sphinx file). This will probably require that you set up your test data separately all at once, delete the indexes (using ruby shell commands) and then force the reindexing (waiting for the total number of indexed documents to be the same as the known amount of database records) and run your tests against the data.

I would really caution against testing the index like I said you might just find yourself having to deal with a constantly breaking test or slow test runtime (depending on the amount of data you are indexing)

christkv
A: 

this is probably more hassle than what it's worth, but have you tried turning on delta indexes?

MatthewFord
+2  A: 

I see the problem being in this statement:

the data is loaded and then rolled back in a transaction during a typical cucumber test so there is no way for thinking_sphinx to index it

It may not be fast to have thinking_sphinx index the results, but it's certainly possible within the transaction. Since it's a single integration test, and not done for each of your (many) unit tests, I'd take the speed hit.

So now you need to figure out how to trigger that re-index during a transaction.

# somewhere in /features/support:
before('@reindexing') do
  require 'Rake'

  class MyModel
    after_save :force_reindex!

    def force_reindex!
      # in case multiple runs of this block cause the hook
      # to get added multiple times, let's check to make sure
      # we haven't already reindexed for this save
      return if @__reindexed
      Rake["thinking_sphinx:rebuild"].execute
      @__reindexed = true
    end
  end
end

after('@reindexing') do
  class MyModel
    def force_reindex!
      # do nothing; the hook still gets called, but that's ok
    end
  end
end

In /features/integration.feature (or whatever), you'd have

@reindexing
Feature: The whole shebang, altogether

  Scenario: ...
James A. Rosen
Ooh, there's a bug in my code! The before('@reindexing') adds the reindexing, but there's no corresponding after to remove it! If you run the whole test suite (integration + units), this will _dramatically_ slow you down.
James A. Rosen
That's not a big deal. I pull out the thinking_sphinx dependent tests using tags and run them separately.
srboisvert
A: 

I tried the suggested solution but sphinx times out with the following error. I don't think it is possible to re-index sphinx while transactional fixtures are enabled.

ERROR: index 'document_core': sql_query_pre[0]: Lock wait timeout exceeded; try restarting transaction

+2  A: 

We were able to get our cucumber tests to work successfully with thinking sphinx by using a database cleaner plugin and modifying our features/support/env.rb as follows:

# Sets up the Rails environment for Cucumber
ENV["RAILS_ENV"] ||= "cucumber"

...

# http://github.com/bmabey/database_cleaner
require 'database_cleaner'
DatabaseCleaner.strategy = :truncation
Before do
  DatabaseCleaner.clean
end

ThinkingSphinx::Configuration.instance.build
ThinkingSphinx::Configuration.instance.controller.start
at_exit do
  ThinkingSphinx::Configuration.instance.controller.stop
end
ThinkingSphinx.deltas_enabled = true
ThinkingSphinx.updates_enabled = true
ThinkingSphinx.suppress_delta_output = true

# Re-generate the index before each Scenario
Before do
  ThinkingSphinx::Configuration.instance.controller.index
end

As you can see, we also created a 'cucumber' environment and a separate database (in order to run cucumber and specs simultaneously without conflicts) - so you will need to add a 'cucumber:' entry to your database.yml and sphinx.yml if you would like to do that as well.

Joshua Kunzmann
+1  A: 

The linked suggestion won't work, because the Rake task invokes the indexer in a seperate process, and so is outside of the test transaction.

I don't know that there's any way around this, other than turning off test transactions so that the sphinx index process can see new and updated records. To do that, in your TestCase, just add the line

self.use_transactional_fixtures = false

Bear in mind that you'll need to manage clearing up any data at the end of your test.

James Adam
A: 

Another example of using tags to seperate cucumber thinking sphinx tests using cucumber tags here.

srboisvert