Alex Chaffee's blog
I'm updating Erector to RSpec 2 and came across two problems for which solutions were surprisingly difficult to Google. Here are my (finally successful) results.
Problem:
no such file to load -- spec/rake/spectask
Before:
require "spec/rake/spectask" # RSpec 1.3
After:
require "rspec/core/rake_task" # RSpec 2.0
Problem:
undefined method `spec_files=' for #<RSpec::Core::RakeTask:0x00000101550aa8>
Before:
# RSpec 1.3
Spec::Rake::SpecTask.new(:core) do |spec|
spec.spec_files = FileList['spec/erector/*_spec.rb']
spec.spec_opts = ['--backtrace']
end
After:
# RSpec 2.0
RSpec::Core::RakeTask.new(:core) do |spec|
spec.pattern = 'spec/erector/*_spec.rb'
spec.rspec_opts = ['--backtrace']
end
See also http://github.com/rspec/rspec-core/blob/master/Upgrade.markdown (curiously cloaked from Google searches for the above problem strings).
When two strings fail to match, if the difference is somewhere in the middle of the strings, it can be annoying/impossible to track down the
actual difference. I've written a little Comparison object that
overrides the failure message for .should == like this:
Strings differ at position 12:
expected: ..."efghijklmnopqrst"...
actual: ..."efghijklXXopqrst"...
It shows a "prelude" of a few characters, then the difference, lined up on successive lines so they're easy to visually scan. It also does the right thing (or tries to) if the difference is near the beginning or end of the string (i.e. does or doesn't show ellipses).
For people who can't wait for this to get incorporated into RSpec
proper, you can grab the code from github and require "comparison" in your spec_helper.rb and it'll override the existing RSpec == matcher. Or wait for Issue 9 to be pulled into version 2.1 (maybe). Or if you want to use it in your favorite testing framework, the object is completely self-contained and should be easy to call from your own assert_equals or whatever.
One open question is whether the exception message should show the full actual string as well as the comparison... On one hand, it adds to screen clutter, but on the other hand, it can be important in tracking down the problem, especially if the prelude is ambiguous.
def capturing_output
output = StringIO.new
$stdout = output
yield
output.string
ensure
$stdout = STDOUT
end
then...
it "exits immediately from --version" do
output = capturing_output do
lambda {
Erector.new(["--version"])
}.should raise_error(SystemExit)
end
output.should == Erector::VERSION + "\n"
end
Here's an RSpec trick I discovered yesterday. Sometimes when you're writing a test you want to loop over some precondition data. But if you do a loop inside your test (or spec), then all the cases will be subsumed in a single test method (or "it" block). This means you'll have the following problems:
- The first case to fail will cause the rest of the cases not to run. It'd be nice to see them all in a single test run.
- You won't take advantage of RSpec's cool self-documenting trick of labeling each it block with a full description of the failure, and it'll be harder to debug which case failed.
- If you're calling into Rails (e.g. in a View spec), you'll only be able to call certain methods -- especially
render-- once per test method. That means that you simply can't use a loop inside a method to collapse redundant tests into a single block.
Ruby to the rescue! Instead of looping inside your it block, loop outside your it block.
