My Behavior Driven Application Setup
April 20, 2009 · by Josh Swan
Last week I showed how I setup for tools for behavior driven development on my MacBook Pro, Behavior Driven Development Environment. In this article I am going to go over how I setup a Rails application for behavior driven development.
Create Rails Application
For the context of the article lets say we are going to build a Rails application called “Super Recipes”. First let’s create the Rails app.
rails SuperRecipes cd SuperRecipes
Configure Testing Gems
Once the Rails project is created I can reference any testing gems we need from within the app using a new feature in Rails 2.3.
Openconfig\environment.rb, you should see a section that looks something like this:
# Specify gems that this application depends on and have them installed with rake gems:install # config.gem "bj" # config.gem "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net" # config.gem "sqlite3-ruby", :lib => "sqlite3" # config.gem "aws-s3", :lib => "aws/s3"
As the comment states, any gems configured in here can be installed by running rake gems:install. However, I have already installed the gems I need using the gem install command (in my last blog article). I didn’t use the rake gems:install mechanism because I was having trouble getting the RSpec, Cucumber, ZenTest gems working correctly together. Given that, it is still beneficial to list the gems you have already installed because Rails will let developers know if they are missing any of the configured gems. It also allows you to run rake gems RAILS_ENV=test to more proactively check if everything is installed. The RAILS_ENV=test parameter tells rake to only check the gems configured in the config/environments/test.rb environment file, which is where I have configured all my testing related gems. I added the following configuration to the end of my test.rb file.
config.gem "sqlite3-ruby", :lib => "sqlite3", :version => '1.2.4' config.gem 'rspec', :lib => false, :version => '1.2.2' config.gem 'rspec-rails', :lib => false, :version => '1.2.2' config.gem 'cucumber', :lib => false, :version => '0.3.0' config.gem 'webrat', :lib => false, :version => '0.4.4' config.gem 'rcov', :lib => false, :version => '0.8.1.2.0' config.gem 'mocha', :lib => false, :version => '0.9.5' config.gem 'ZenTest', :lib => false, :version => '4.0.0'
Once I have those configured I can run the rake gems RAILS_ENV=test. This will show me the results below. If I had a missing gem or an incorrect version I would have received an error message.
– [I] sqlite3-ruby = 1.2.4
– [I] rspec = 1.2.2
– [I] rspec-rails = 1.2.2
– [I] rspec = 1.2.2
– [I] rack >= 0.4.0
– [I] cucumber = 0.3.0
– [I] term-ansicolor >= 1.0.3
– [I] treetop >= 1.2.5
– [I] polyglot
– [I] polyglot >= 0.2.5
– [I] diff-lcs >= 1.1.2
– ® builder >= 2.1.2
– [I] webrat = 0.4.4
– [I] nokogiri >= 1.2.0
– [I] rcov = 0.8.1.2.0
– [I] mocha = 0.9.5
– ® rake
– [I] ZenTest = 4.0.0
I = Installed
F = Frozen
R = Framework (loaded before rails starts)At this point I could also unpack my configured gems and their dependencies to the vendor/gems directory by running these commands. I’m going to skip doing this since I don’t see any reason to right now.
rake gems:unpack RAILS_ENV=test rake gems:unpack:dependencies RAILS_ENV=test
I am also going to install the annotate_models plugin by Dave Thomas. Running this will annotate the models, fixtures, and unit tests with the database structure. RSpec support was recently added so it also annotates the model specs. This comes in handy since there are no methods or instance variables variables in the models for the database fields. Run the following in the projects folder to install the plug-in into the project’s vendor folder and run the annotate_models rake task on the project. If you change your database structure, simple rerun the task to update the annotations.
script/plugin install http://repo.pragprog.com/svn/Public/plugins/annotate_models rake annotate_models
Setup Development and Testing Databases
I will be using sqlite3 for my development and testing databases. Luckily since sqlite is the default database for Rails there is very little for me to do. To setup the sqlite database files for development and testing I can run the rake task below. Two new.sqlite3 database files will be created in the db folder.
rake db:create:all
Setup Testing Frameworks
I will be using Cucumber and RSpec exclusively for testing so I am going to delete the defaulttest directory. This directory is for tests written using the testing framework built into Rails.
rm -rf test
Next I can setup the project to use Cucumber and RSpec by executing their generators.
script/generate cucumber script/generate rspec
To make sure Cucumber works I runcucumber features. The output should look like this if no features have been created yet.
0 scenarios
RSpec Setup
Personally I like to use Mocha as my mocking framework over RSpecs built in one so I need to let RSpec know that. If you openspec/spec_helper.rb you will see the section below. All I have to do to enable Mocha for testing is to uncomment the config.mock_with :mocha line.
# == Mock Framework # # RSpec uses it's own mocking framework by default. If you prefer to # use mocha, flexmock or RR, uncomment the appropriate line: # # config.mock_with :mocha # config.mock_with :flexmock # config.mock_with :rr
Also, I need to exclude the features directory from being checked by RSpec. That way if I ever run the RSpec’s spec rake task it will not run any of the feature specs. I can do this by modifying the spec/rcov.opts file to include the features directory in the exclude option.
--exclude "spec/*,gems/*,features/*" --rails
ZenTest Setup
ZenTest contains a command calledautotest which will run my features and specs for code I change automatically. To have ZenTest’s autotest command run my Cucumber features I need to set the AUTOFEATURE environment variable to true. The easiest way to do this is to prefix the autotest command with it.
AUTOFEATURE=true autotest
Another option I have is to set it more globally in my ~/.bash_profile file. This way I don’t need to prefix autotest all the time. However, this configuration does enable autotest’s execution of features across all projects on your computer.
export AUTOFEATURE=true
RCov Setup
RSpec already has a rake task for running rcov,spec:rcov, so we shouldn’t have to worry about setting up RCov with RSpec. Cucumber, however, does not have a rake task for RCov. This means one has to be added to the lib/tasks/cucumber.rake file. Below is what my cucumber.rake file looks like after I made modifications to it. As you can see I created a features namespace that now contains two tasks: all and rcov. The all task is the same as the old cucumber features task and it has been aliased so cucumber features or rake features commands still work as expected. The rcov tasks now runs all the features with RCov enabled so to run RCov on my features I simple call rake features:rcov.
$LOAD_PATH.unshift(RAILS_ROOT + ‘/vendor/plugins/cucumber/lib’) if File.directory?(RAILS_ROOT + ‘/vendor/plugins/cucumber/lib’)
begin desc “Run all features” task :features => ‘db:test:prepare’ task :features => “features:all” require ‘cucumber/rake/task’ namespace :features do Cucumber::Rake::Task.new(:all) do |t| t.cucumber_opts = “—format pretty” end desc ‘Run all features with code coverage’ Cucumber::Rake::Task.new(:rcov) do |t| t.rcov = true t.rcov_opts = %w{—rails —exclude osx\/objc,gems\/,spec\/,features\/} t.rcov_opts << %[-o “features_coverage”] end end rescue LoadError desc ‘Cucumber rake task not available’ task :features do abort ‘Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin’ end end
Git Repository Setup
Now that I have the project setup it is time to setup the project’s Git repository and check in the code. First I need to initialize the repository. Since my working directory is also my local repository with Git, I will need to make sure I am in my project’s root folder, SuperRecipe, before initializing.
cd SuperRecipe git init
With my repository initialized, I want to make sure I ignore any log, temp, or generated files that I do not want added to my repository. To do this I need to create a .gitignore file.
mate .gitignore
Now I need to add the files to ignore. Below is a fairly common ignore configuration for a Rails project. Notice I have also added the rspec coverage and cucumber features_coverage folders since the code coverage files in them are generated. I also added config/database.yml. I did this because different developers may have to customize their database configuration for their development machines so we would not want to check-in every developers custom database settings. The developers would keep stepping on each others toes. Instead I have renamed the database.yml file to database_example.yml file that developers can rename to database.yml when they check out the project to their machine. (Thanks Brian for pointing that out)
log/*.log coverage/* features_coverage/* tmp/**/* doc/api doc/app config/database.yml
Since Git only stores files, any folders that are empty will not be store in the repository. This presents a problem for the log and tmp folders that we want to be available to anyone who checks out the project. To fix this we need to add an empty .gitignore file to each folder.
touch log/.gitignore touch tmp/.gitignore
The project should be ready to be checked in now. To do this first we must stage the project using the add command.
git add .
If you run git status after an add you can check that none of the ignored files are going to be checked in. I can now check my project into the repository by doing a commit.
git commit -m "Initial project commit"
Conclusion
All I need to do now is to start my features and work outside in to develop your application. I hope this article has helps you get setup for behavior driven development. If you would like to know more about how to write cucumber features check outhttp://cukes.info/. Also, there is a new book called The RSpec Book at Pragmatic Programmers that covers BBD and many of the tools I used here.
Filed in: Team Member Blog Comments (2)
Comments
- #2 - Josh Swan said on Apr 21, 2009:
- Thanks for your input. Both are really good points and I will update my article.
-
Commenting is not available in this section entry.