today I’m releasing test/spec 0.10, a library to do BDD with Test::Unit.
== News in 0.10:
- February 1st, 2009: Sixth public release 0.10.
- Support for Ruby 1.9. You must have the test-unit gem on 1.9.
== What is test/spec?
test/spec layers an RSpec-inspired interface on top of Test::Unit, so
you can mix TDD and BDD (Behavior-Driven Development).
test/spec is a clean-room implementation that maps most kinds of
Test::Unit assertions to a `should’-like syntax.
Consider this Test::Unit test case:
class TestFoo < Test::Unit::TestCase def test_should_bar assert_equal 5, 2 + 3 end end
In test/spec, it looks like this:
require 'test/spec' context "Foo" do specify "should bar" do (2 + 3).should.equal 5 end end
Since test/spec 0.4, you can also use the new RSpec 1.0 style:
require 'test/spec' describe "Foo" do it "should bar" do (2 + 3).should.equal 5 end end
test/spec does not include a mocking/stubbing-framework; use whichever
you like to. test/spec has been tested successfully with FlexMock and
test/spec has no dependencies outside Ruby 1.8.
== Mixing test/spec and test/unit
test/spec and Test::Unit contexts/test cases can be intermixed freely,
run in the same test and live in the same files. You can just add them
to your Rake::TestTask, too. test/spec allows you to leverage your
full existing Test::Unit infrastructure.
test/spec does not change Test::Unit with the exception of
monkey-patching Test::Unit::TestSuite to order the test cases before
running them. (This should not do any harm, but if you know a way
around it, please tell me.)
== Wrapped assertions
assert_equal: should.equal, should ==
assert_not_equal: should.not.equal, should.not ==
assert_match: should.match, should =~
assert_no_match: should.not.match, should.not =~
== Additional assertions
These assertions are not included in Test::Unit, but have been added
to test/spec for convenience:
- a.should. (works like assert a.?)
- a.should.be (where is <, <=, >, >=, or ===)
- should.output, to check what is printed
With more complex assertions, it may be helpful to provide a message
to show if the assertion has failed. This can be done with the
Should#blaming or Should#messaging methods:
RUBY_VERSION.should.messaging("Ruby too old.").be > "1.8.4" (1 + 1).should.blaming("weird math").not.equal 11
== Custom shoulds (“Matchers”)
To capture recurring patterns in parts of your specifications, you can
define custom “shoulds” (RSpec calls them “matchers”) in your
contexts, or include modules of them:
context "Numbers" class EqualString < Test::Spec::CustomShould def matches?(other) object == other.to_s end end def equal_string(str) EqualString.new(str) end specify "should have to_s" 42.should equal_string("42") end end
Alternatively, your implementation can define
CustomShould#assumptions, where you can use test/spec assertions
instead of Boolean predicates:
class EqualString < Test::Spec::CustomShould def assumptions(other) object.should.equal other.to_s end end
A CustomShould by default takes one argument, which is placed in
self.object for your convenience.
You can CustomShould#failure_message to provide a better error
== SpecDox and RDox
test/spec adds two additional test runners to Test::Unit, based on the
console runner but with a different output format.
SpecDox, run with --runner=specdox (or -rs) looks like RSpec’s output:
should.output - works for print - works for puts - works with readline
RDox, run with --runner=rdox (or -rr) can be included for RDoc
== should.output * works for print * works for puts * works with readline
SpecDox and RDox work for Test::Unit too:
$ ruby -r test/spec test/testunit/test_testresult.rb -rs Test::Unit::TC_TestResult - fault notification - passed? - result changed notification Finished in 0.106647 seconds. 3 specifications (30 requirements), 0 failures
== Shared contexts
Since version 0.9, you can define shared contexts in test/spec using
shared_context/describe_shared. These contexts are not executed on
their own, but can be included with it_should_behave_like/behaves_like
in other contexts. You can use shared contexts to structure suites
with many recurring specifications.
== Thanks to
- Eero S. for writing should.output.
- Tuxie for writing test/spec on Rails.
- Brian Donovan for allowing alternative superclasses.
- Xavier S. for implementing nested setups/teardowns.
- Chris W. for should.raise with a block and xcontext.
- Jean-Michel Garnier for packaging the first gem.
- Mikko L., Jan Wikholm, Matt M. and Michael F. for
testing the gem.
- Chris McGrath for reporting a bug.
- Thomas F. for script.aculo.us BDD testing which convinced me.
- Dave A. for BDD.
- The RSpec team for API inspiration.
- Nathaniel T. for Test::Unit.
Copyright © 2006, 2007, 2008, 2009 Christian N.
test/spec is licensed under the same terms as Ruby itself.
== Where can I get it?
You can download test/spec 0.10 at
Alternatively, you can checkout from the development repository with:
darcs get http://chneukirchen.org/repos/testspec
Please mail bugs, suggestions and patches to
(Patches using “darcs send” are most welcome.)
== Installing with RubyGems
Since version 0.3, a Gem of test/spec is available. You can install
gem install test-spec
(It may take some time for the index to be updated and the mirrors
propagated.) I also provide a local mirror of the gems (and
development snapshots) at my site:
gem install test-spec --source
Behavior-Driven Development:: http://behaviour-driven.org/
Happy hacking and have a nice day,