Saturday, April 18, 2009

One Step Back

‹prev | My Chain | next›

Looking for the next scenario on which to work, I run all scenarios:
cstrom@jaynestown:~/repos/eee-code$ cucumber features/recipe_search.feature -n
Feature: Search for recipes

So that I can find one recipe among many
As a web user
I want to be able search recipes
Scenario: Matching a word in the ingredient list in full recipe search
Given a "pancake" recipe with "chocolate chips" in it
And a "french toast" recipe with "eggs" in it
And a 1 second wait to allow the search index to be updated
When I search for "chocolate"
Then I should see the "pancake" recipe in the search results
And I should not see the "french toast" recipe in the search results

Scenario: Matching a word in the recipe summary
Given a "pancake" recipe with a "Yummy!" summary
And a "french toast" recipe with a "Delicious" summary
And a 1 second wait to allow the search index to be updated
When I search for "yummy"
Then I should see the "pancake" recipe in the search results
expected following output to contain a <a href='/recipes/id-pancake'>pancake</a> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><table><tr>
<th>Name</th>
<th>Date</th>
</tr></table></body></html> (Spec::Expectations::ExpectationNotMetError)
./features/step_definitions/recipe_search.rb:82:in `Then /^I should see the "(.+)" recipe in the search results$/'
features/recipe_search.feature:22:in `Then I should see the "pancake" recipe in the search results'
And I should not see the "french toast" recipe in the search results

Scenario: Matching a word stem in the recipe instructions
Given a "pancake" recipe with instructions "mixing together dry ingredients"
And a "french toast" recipe with instructions "whisking the eggs"
And a 1 second wait to allow the search index to be updated
When I search for "whisk"
Then I should not see the "pancake" recipe in the search results
And I should see the "french toast" recipe in the search results
expected following output to contain a <a href='/recipes/id-french-toast'>french toast</a> tag:
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" "http://www.w3.org/TR/REC-html40/loose.dtd">
<html><body><table><tr>
<th>Name</th>
<th>Date</th>
</tr></table></body></html> (Spec::Expectations::ExpectationNotMetError)
./features/step_definitions/recipe_search.rb:82:in `And /^I should see the "(.+)" recipe in the search results$/'
features/recipe_search.feature:32:in `And I should see the "french toast" recipe in the search results'

...
7 scenarios
2 scenarios pending
16 steps passed
2 steps failed
7 steps skipped
5 steps pending (5 with no step definition)
Hunh? I just got those two scenarios passing, why are they failing?

After much trial and error investigation, I find that couchdb-lucene (or CouchDB itself) requires some settle time in between database tear-down and puts. I am not happy with that as an explanation, but it serves as an accurate observation. At time permits, I will go back and dig up a satisfactory explanation.

Back when I first got cucumber to drive Sinatra / CouchDB tests, I added the database puts and tear-downs as Before and After blocks to features/support/env.rb. Experimentation proves that sleeping for half a second after a tear-down resolves the problem:
After do
RestClient.delete @@db
sleep 0.5
end
Lower sleep times work sporadically, while half a second always seems to work.

I did try to add a sleep to the beginning of the scenarios—something along the lines of "Given a 0.5 second wait for the newly created database to settle". That always failed. This leads me to believe that there is some thread in CouchDB that is not awakening in between rapid deletes and recreates of the database. The end result being that couchdb-lucene is completely unaware that it needs to re-index.

To ease the blow of having to wait 0.5 seconds after every tear-down (after every scenario), I also tweak the sleep that I added in the search scenarios (e.g. "Given a 1 second wait to allow the search index to be updated"). That delay was added to allow couchdb-lucene to index newly added documents (as opposed to recognizing newly created databases). Rather than wait a full second for the newly created documents to be indexed, I change them to wait 0.25 seconds. To accommodate this change, the step definition needs to honor floats rather than integers:
Given /^a ([.\d]+) second wait/ do |seconds|
sleep seconds.to_f
end
Et violà, all the finished scenarios pass when run together:
Feature: Search for recipes

So that I can find one recipe among many
As a web user
I want to be able search recipes
Scenario: Matching a word in the ingredient list in full recipe search
Given a "pancake" recipe with "chocolate chips" in it
And a "french toast" recipe with "eggs" in it
And a 0.25 second wait to allow the search index to be updated
When I search for "chocolate"
Then I should see the "pancake" recipe in the search results
And I should not see the "french toast" recipe in the search results

Scenario: Matching a word in the recipe summary
Given a "pancake" recipe with a "Yummy!" summary
And a "french toast" recipe with a "Delicious" summary
And a 0.25 second wait to allow the search index to be updated
When I search for "yummy"
Then I should see the "pancake" recipe in the search results
And I should not see the "french toast" recipe in the search results

Scenario: Matching a word stem in the recipe instructions
Given a "pancake" recipe with instructions "mixing together dry ingredients"
And a "french toast" recipe with instructions "whisking the eggs"
And a 0.25 second wait to allow the search index to be updated
When I search for "whisk"
Then I should not see the "pancake" recipe in the search results
And I should see the "french toast" recipe in the search results

Scenario: Searching titles
...
7 scenarios
2 scenarios pending
19 steps passed
6 steps skipped
5 steps pending (5 with no step definition)

(commit)

Fork couchdb-lucene

I forked Robert Newson's excellent couchdb-lucene to include my stemming analyzer. I wanted to be able to track his changes (of which there are many), while keeping my changes in there. Forking make this easy.

Forking may also afford a chance to investigate how to get it working better with quick turnaround tear-down / database puts that are needed in cucumber scenarios. But that is for another day. Maybe.

2 comments:

  1. Hey, I think your post series is great. It'd help if you'd report any CouchDB problems in the CouchDB issue tracker:

    http://issues.apache.org/jira/browse/COUCHDB

    Cheers
    Jan
    --

    ReplyDelete
  2. Thanks Jan! I'm very appreciative of all the help that you and the community have provided along the way.

    If I run into any problems, I will certainly report them (want to give back to the community that's given me so much). So far though, I have yet to hit any problems—CouchDB performs at or well beyond what I expect / desire.

    I don't think rapidly deleting, then recreating a database and not having a 3rd party realize the difference amounts to much of a defect. Perhaps that is just my still limited understanding of things. I'll keep digging to see if there really is a problem.

    ReplyDelete