RSpec at github 8
After a few months of exploring git and hosting RSpec’s git repository at github, we’re happy to announce that github is now RSpec’s official home for Source Code Management.
Tracking will continue to live at the lighthouse.
We will continue to release gems to http://rubyforge.org/projects/rspec, but we will no longer be committing changes to the subversion repository there. For Rails users who are using the rspec plugins for Rails, edge rails now supports git-hosted plugins.
We’ve broken the project up into four separate repositories:
- rspec for the rspec gem/plugin
- rspec-rails for the rspec-rails gem/plugin (formerly rspec_on_rails)
- rspec-tmbundle for the TextMate bundle
- rspec-dev for developers/contributors
See the wikis for each repository for more information about building, installing and contributing to the project.
Welcome Pat Maddox 1
I’m pleased to announce that Pat Maddox is joining the RSpec Development Team.
As you may already know, Pat has been contributing great patches and actively participating on the rspec-users and rspec-devel mailing lists for quite some time. He has demonstrated a deep understanding of Behaviour Driven Development in general, and specifically as it applies to Rails, which has certainly posed some of the more interesting questions on our mailing lists.
We are all excited to have Pat on board and look forward to his continued contribution.
limiting scope of autotest 17
If you use autotest with rspec or test/unit, you’ve probably had this experience (or one like it):
You want to add some new behaviour to a model object, so you write a spec, watch it fail, make it pass, and then wait until the entire spec suite runs. Even if you’ve got a fast-running suite, this can be painful sometimes.
Wouldn’t it be great if you could limit the scope of what directories autotest observes? Well it turns out that you can! Recent releases of ZenTest include a find_directories attribute on the autotest object. Just add this to your .autotest file:
Autotest.add_hook :initialize do |at|
unless ARGV.empty?
at.find_directories = ARGV.dup
end
end
and then you can say:
autotest app/models spec/models
and it will only observe those directories. This is nice and flexible, but I find that most of the time I’m wanting pairs like that: app/models and spec/models, or app/views/accounts and spec/views/accounts. In that case, I’d really like to just say:
autotest models
To accomplish that you can do this to the hook instead:
Autotest.add_hook :initialize do |at|
unless ARGV.empty?
at.find_directories = ["spec/#{ARGV.first}","app/#{ARGV.first}"]
end
end
Want the best of both worlds? Try this:
Autotest.add_hook :initialize do |at|
unless ARGV.empty?
at.find_directories = ARGV.length == 1 ? ["spec/#{ARGV.first}","app/#{ARGV.first}"] : ARGV.dup
end
end
The only limitation of this is that it’s based on directories, not files. Once in a while, when I’m bootstrapping a new object, I’ll keep the examples and the implementation in the same file until I’ve got things fleshed out a bit the object is ready to play nice with others. In that case, I might like to just point autotest to that one file. I started working on a patch for this for ZenTest, but I’m not sure it’s worth the extra effort. What do you think?
Regardless – happy auto-exemplifying!
RSpec-1.1.3 and ZenTest-3.9.1 6
ZenTest’s last two releases are not compatible with previous versions of RSpec. This is good news because Autotest now exposes better extension points for subclasses like those that ship with RSpec. Before, RSpec had to monkey patch Autotest to control the mappings of specs to files to run, and the list of files/directories to ignore. Now RSpec gets to use public methods (instead of instance variables) and documented hooks to do it’s work.
In the long run, this will keep things more flexible for both RSpec and ZenTest. In the short run, the catch for you is that you have to use compatible versions of RSpec and ZenTest. They are:
| RSpec version | ZenTest version |
|---|---|
| <= 1.1.1 | <= 3.7.x |
| 1.1.2 | 3.8.x |
| 1.1.3 | 3.9.x |
RSpec: new --pattern option 1
I’m not in the habit of blogging every change we make to RSpec, but this one may change the way your suite behaves if you have not been following convention.
As of RSpec’s trunk revision 3246 there is a new command line option that lets you control the filename pattern to match. This allows you to restrict files that are loaded when running the spec command in the same way that you can with rake. It also means that helper files that you may depend on are no longer loaded implicitly.
The default is:
"**/*_spec.rb".
To get rspec to behave as it did before this change, use this:
--pattern "**/*.rb"
If you prefer naming your spec files “foo_example.rb”, you can do this:
--pattern "**/*_example.rb"
You can also supply multiple patterns (comma separated):
--pattern "**/*_example.rb, **/*_spec.rb"
This is a very handy way to avoid loading resource files (helpers, matchers, etc) except when you require them explicitly from other files.
This is currently only in trunk, but will be part of the next release.
RSpec-1.1.2 and ZenTest-3.8.0 11
The RSpec-1.1.2 release includes changes to keep RSpec compatible with autotest in ZenTest-3.8.0. This new ZenTest release boasts an improved cascading configuration model that works well for subclasses (like those that ship with RSpec) and allows users to override the mappings of specs (or tests) to code as well as the list of files that get ignored by autotest.
To support this, Autotest now loads the following files in the following order:
Autotest
AutotestSubClass
~/.autotest
./.autotest
This allows RSpec (or any other library) to override defaults set in Autotest, and then provides users both generic (~/.autotest) and project specific (./.autotest) control over the mappings and exceptions.
How can you take advantage of this?
When autotest begins to run, it calls its :initialize hook. This hook is exposed by the add_hook method. You can use this to access the mappings and exceptions using the following methods on Autotest:
clear_mappings()
add_mapping(regexp, proc)
remove_mapping(regexp)
clear_exceptions()
add_exception(string)
remove_exception(string)
add_mapping
The add_mapping method adds a key/value pair to a hash that maps regexps to procs. Whenever autotest senses that a file is touched, it looks for the regexp that matches the file name and the runs all the files returned by the associated proc.
Imagine you’re working on a shopping cart app. You have some currency conversion behaviour in a Product model that you’d like to extract to an acts_as_currency plugin, and you want autotest to observe the process. You might add a mapping like this to .autotest:
Autotest.add_hook :initialize do |at|
at.add_mapping(%r%^plugins/acts_as_currency/lib/.*\.rb$%) {
at.files_matching %r%^spec/models/product_spec\.rb$% +
at.files_matching %r%^plugins/acts_as_currency/spec/.*_spec\.rb$%
}
end
In this case, a change to any of the files in the plugin’s lib directory would cause all the plugins specs to run, as well as the spec for the Product model.
add_exception
The add_exception method adds paths to a list of paths that Autotest ignores.
I like to run autotest in verbose mode (autotest -v) because it tells me when I change a file that it doesn’t know what to do with. The drawback is that it wants to tell me every time I commit because files in the .svn/.hg/.git directories change. So I’ve got these all listed as exceptions in my ~/.autotest file, along with assorted others:
Autotest.add_hook :initialize do |at|
%w{.svn .hg .git}.each {|exception|at.add_exception(exception)}
end
Note that autotest compiles this list to a Regexp with no anchors, so .hgignore and .gitignore would also get ignored in this case.
Cascading config and granular control
One of the coolest changes in ZenTest-3.8.0 is that autotest loads both ~/.autotest and ./.autotest. So now you can have the hooks you like on every project (like growl notifation) all in one place and still have project specific settings.
This also allows you to set up global mappings/exceptions and modify them at the project level. See Autotest’s RDoc for more info.
Advanced Rails Recipes 2
I’m pleased to announce that my contribution to Mike Clark’s new Advanced Rails Recipes book has been accepted and released. The book is available right now as a Beta PDF and includes dozens of delicious and nutritious recipes for enhancing your Rails applications and the process of developing them.
This is my first formal publication in the software world [1], and I couldn’t be more pleased than to have it be a Pragmatic Bookshelf [2] publication.
My recipe is entitled Describing Behaviour from the Outside-In With RSpec and it demonstrates the BDD approach to Rails apps starting with the view and working your way down to the controllers, models and database.
There is currently one other BDD recipe: Getting Started with BDD, which uses shoulda. There are also recipes for cooking up mocks, code coverage and html validity. And that’s just the testing related recipes.
There are also dozens of recipes dealing with UI, search, email, console, REST, db enhancements and even more general design improvements.
Like the first Rails Recipes book, this one is a must-have for any serious Rails developer who wants to take it up a notch in creating great web applications with Rails.
[1] My first publication of any kind was back around 1980 when I was a young professional magician. The book is called, simply, Coin Magic, and is a must have for any serious coin magician who wants to take it up a notch (see a trend here?) in presenting awesome feats of magic with ordinary coins. Back then I went by my first and middle name, David Arthur.
[2] The Pragmatic Programmers are also publishing my upcoming book with co-author Aslak Hellesøy, tentatively entitled Behaviour Driven Development in Ruby with RSpec.
RSpec 1.1 17
The RSpec Development Team is pleased as glug (that’s kind of like punch, but more festive) to announce RSpec-1.1.0.
Thanks to all who have contributed patches over the last few months. Big thanks to Dan North and Brian Takita for their important work on this release. Dan contributed his rbehave framework which is now the Story Runner. Brian patiently did a TON of refactoring around interoperability with Test::Unit, and the result is a much cleaner RSpec core, and a clean adapter model that gets loaded when Test::Unit is on the path.
RSpec 1.1 brings four significant changes for RSpec users:
- The RSpec Story Runner
- Nested Example Groups
- Support for Rails 2.0.1
- Test::Unit interoperability
Story Runner
The RSpec Story Runner is Dan North’s rbehave framework merged into RSpec. The Story Runner is a framework for expressing high level requirements in the form of executable User Stories with Scenarios that represent Customer Acceptance Tests.
RSpec 1.1 also ships with a Ruby on Rails extension called RailsStory, which lets you write executable user stories for your rails apps as well.
Nested Example Groups
Now you can nest groups to organize things a bit better:
describe RubyDeveloper do
before(:each) do
@ruby_developer = RubyDeveloper.new
end
describe "using RSpec 1.1.0" do
before(:each) do
@ruby_developer.use_rspec('1.1.0')
end
it "should be able to nest example groups" do
@ruby_developer.should be_able_to_nest_example_groups
end
end
describe "using RSpec 1.0.1" do
before(:each) do
@ruby_developer.use_rspec('1.0.8')
end
it "should not be able to nest example groups" do
@ruby_developer.should_not be_able_to_nest_example_groups
end
end
end
Running this outputs:
RubyDeveloper using RSpec 1.1.0 - should be able to nest example groups RubyDeveloper using RSpec 1.0.8 - should not be able to nest example groups
== Support for Rails 2.0.1
gem install rails rails myapp ruby script/plugin install http://rspec.rubyforge.org/svn/tags/REL_1_1_0/rspec ruby script/plugin install http://rspec.rubyforge.org/svn/tags/REL_1_1_0/rspec_on_rails script/generate rspec
Test::Unit Interoperability
Contrary to popular belief, Spec::Rails, RSpec’s Ruby on Rails plugin, has been a Test::Unit wrapper since the the 0.7 release in November of 2006. RSpec 1.1 ups the ante though, offering a smooth transition from Test::Unit to RSpec with or without Rails:
1. Start with a TestCase:
require 'test/unit'
class TransitionTest < Test::Unit::TestCase
def test_should_be_smooth
transition = Transition.new(
:from => "Test::Unit::TestCase",
:to => "Spec::ExampleGroup"
)
assert_equal "really smooth", transition.in_practice
end
end
2. Require ‘spec’
require 'test/unit'
require 'spec'
class TransitionTest < Test::Unit::TestCase
def test_should_be_smooth
transition = Transition.new(
:from => "Test::Unit::TestCase",
:to => "Spec::ExampleGroup"
)
assert_equal "really smooth", transition.in_practice
end
end
3. Convert TestCase to ExampleGroup
require 'test/unit'
require 'spec'
describe "transitioning from TestCase to ExampleGroup" do
def test_should_be_smooth
transition = Transition.new(
:from => "Test::Unit::TestCase",
:to => "Spec::ExampleGroup"
)
assert_equal "really smooth", transition.in_practice
end
end
4. Convert test methods to examples
require 'test/unit'
require 'spec'
describe "transitioning from TestCase to ExampleGroup" do
it "should be smooth" do
transition = Transition.new(
:from => "Test::Unit::TestCase",
:to => "Spec::ExampleGroup"
)
assert_equal "really smooth", transition.in_practice
end
end
5. Convert assertions to expectations
require 'test/unit'
require 'spec'
describe "transitioning from TestCase to ExampleGroup" do
it "should be smooth" do
transition = Transition.new(
:from => "Test::Unit::TestCase",
:to => "Spec::ExampleGroup")
transition.in_practice.should == "really smooth"
end
end
6. Un-require test/unit
require 'spec'
describe "transitioning from TestCase to ExampleGroup" do
it "should be smooth" do
transition = Transition.new(
:from => "Test::Unit::TestCase",
:to => "Spec::ExampleGroup"
)
transition.in_practice.should == "really smooth"
end
end
At every one of these steps after step 2, you can run the file with the ruby command and you’ll be getting RSpec’s developer friendly output. This means that you can transition things as gradually as you like: no wholesale changes.
That’s the story. Thanks again to all who contributed and to all who continue do so.
RubyConf 2007 Talk 2
The RSpec/BDD talk that Dave Astels and I presented at RubyConf 2007 has been posted for your viewing pleasure.
There are also a bunch of other RubyConf talks posted on the same site.
One correction: In our talk, I said that the role/feature/reason story format came from Mike Cohn’s book User Stories Applied. The real source was a project team at Connextra that Tim MacKinnon was a part of. Tim later joined Thoughtworks, where Dan North learned about it from Tim.
I did, in fact, learn about it from Mike Cohn, but it was at a talk he did at an Agile Conference a couple of summers back. I had also read his book, and somewhere in my head merged the talk and the book.
Nested Example Groups 10
Since rspec first appeared on the scene, users have been asking for nested example groups. Well it has finally arrived. RSpec 1.1.0 will ship with support for nesting, so you’ll be able to do things like this:
describe RSpec do
before(:each) do
@rspec = RSpec.new
end
describe "at release 1.0.8" do
before(:each) do
@rspec.version = "1.0.8"
end
it "should not support nested example groups" do
@rspec.should_not support_nested_example_groups
end
end
describe "at release 1.1.0" do
before(:each) do
@rspec.version = "1.1.0"
end
it "should support nested example groups" do
@rspec.should support_nested_example_groups
end
end
end
This will output:
RSpec at release 1.0.8 - should not support nested example groups RSpec at release 1.1.0 - should support nested example groups
If you’re using trunk, you can do this now with revision 3009 or later.
Happy nesting!