Continuing the chain, tonight I finish the spike of using Cucumber, Sinatra, and CouchDB together.
Thanks to a hint from Joseph Wilk, I already have a good idea how to work with a CouchDB test instance. Let's see if it pans out...
First I modify the spike.rb Sinatra code to use a different database by adding an environment-dependent db method:
require 'rubygems'A
require 'sinatra'
require 'rest_client'
require 'json'
#####
# Use a different CouchDB instance in test mode
def db
Sinatra::Application.environment == :test ?
"http://localhost:5984/eee-test" :
"http://localhost:5984/eee-meals"
end
get '/meals/:permalink' do
data = RestClient.get "#{db}/#{params[:permalink]}"
result = JSON.parse(data)
%Q{
<h1>#{result['title']}</h1>
<p>We enjoyed this meal on #{result['date']}</p>
<p>#{result['summary']}</p>
<p>navigation and links to recipes would go here...</p>
<div>
#{result['description']}
</div>
}
end
Before
block (which is a Cucumber hook) can be added to create the test DB before each scenario (db is the method from the Sinatra app):Before doSimilarly, an
RestClient.put db, { }
end
After
block can be used to tear down the DB after each scenario:After doThese blocks seem to work fine in the
RestClient.delete db
end
features/support/env.rb
file, which is then:# NOTE: This must come before the require 'webrat', otherwiseLastly, I need to implement the Given step from yesterday's spike (see the discussion in the comments):
# sinatra will look in the wrong place for it's views.
require File.dirname(__FILE__) + '/../../spike'
# RSpec matchers
require 'spec/expectations'
# Webrat
require 'webrat'
Webrat.configure do |config|
config.mode = :sinatra
end
World do
session = Webrat::SinatraSession.new
session.extend(Webrat::Matchers)
session
end
Before do
RestClient.put db, { }
end
After do
RestClient.delete db
end
Given /^a "(.+)" meal$/ do |title|If the Given text reads 'Given a "Breakfast Elves" meal', then the string "Breakfast Elves" will be assigned to the block's title variable. I use that to calculate a permalink (id) for the document by replacing all non-characters with a dash. RestClient puts the new document, with the appropriate title, into the DB.
@permalink = title.gsub(/\W/, '-')
RestClient.put "#{db}/#{@permalink}",
{ :title => title }.to_json,
:content_type => 'application/json'
end
By assigning the permalink to an instance variable, it is re-usable in subsequent steps, in this case the "When I view the meal permalink" step:
When /^I view the meal permalink$/ doThus, then entire steps file for my spike becomes:
visit("/meals/#{@permalink}")
end
Given /^a "(.+)" meal$/ do |title|Now when I run it, I get 3 passing steps without the oogy feeling about that empty Given:
@permalink = title.gsub(/\W/, '-')
RestClient.put "#{db}/#{@permalink}",
{ :title => title }.to_json,
:content_type => 'application/json'
end
When /^I view the meal permalink$/ do
visit("/meals/#{@permalink}")
end
Then /^the title should include "(.+)"$/ do |title|
response.should have_selector("h1", :content => title)
end
Feature: See a mealAll that is left is to rm -rf the spike code.
So that I can see an old meal
As a web user
I want to browse a single meal by permalink
Scenario: View Meal
Given a "Breakfast Elves" meal
When I view the meal permalink
Then the title should include "Breakfast Elves"
1 scenario
3 steps passed
Some Final Thoughts
Creating and deleting the DB for each scenario is a coarse solution compared to using transactions to rollback test data in ActiveRecord tests. Even so, this just might be acceptable.The scenario runs in under 0.5 seconds. That is about what the typical scenario takes in my experience with Rails.
It is not as if any structure needs to be built up in the CouchDB instance upon DB creation—that is one of the points of a document oriented database, after all.
I will have to ruminate on this, but reimplementing EEE Cooks on Sinatra might just work. Then again, it may prove hard to resist a Merb or Rails edge spike over the next couple of nights.
No comments:
Post a Comment