Today I’m releasing test/spec 0.3, a library to do BDD with Test::Unit.
(See below for changes in version 0.3.)
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
test/spec does not include a mocking/stubbing-framework; use whichever you like to—FlexMock and Mocha have been tested.
test/spec has no dependencies outside Ruby 1.8.
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.)
assert_equal
: should.equal
, should ==
assert_not_equal
: should.not.equal
, should.not ==
assert_same
: should.be
assert_not_same
: should.not.be
assert_nil
: should.be.nil
assert_not_nil
: should.not.be.nil
assert_in_delta
: should.be.close
assert_match
: should.match
, should =~
assert_no_match
: should.not.match
, should.not =~
assert_instance_of
: should.be.an.instance_of
assert_kind_of
: should.be.a.kind_of
assert_respond_to
: should.respond_to
assert_raise
: should.raise
assert_nothing_raised
: should.not.raise
assert_throws
: should.throw
assert_nothing_thrown
: should.not.throw
assert_block
: should.satisfy
These assertions are not included in Test::Unit, but have been added to test/spec for convenience:
should.not.satisfy
a.should.predicate
(works like assert
a.predicate?
)a.should.be operator
(where
operator is <
, <=
, >
, >=
, or ===
)should.output
, to check what is printedWith 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
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
message.
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:
spec.output
- works for print
- works for puts
- works with readline
RDox, run with --runner=rdox
(or -rr
) can be
included for RDoc documentation:
== spec.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
Akin to the usual Test::Unit practice, tests quickly can be disabled
by replacing specify
with xspecify
. test/spec will count the
disabled tests when you run it with SpecDox or RDox.
Since version 0.2, test/spec features a standalone test runner called specrb. specrb is like an extended version of testrb, Test::Unit’s test runner, but has additional options. It can be used for plain Test::Unit suites, too.
$ specrb -a -s -n should.output
should.output
- works for print
- works for puts
- works with readline
Finished in 0.162571 seconds.
3 specifications (6 requirements), 0 failures
See specrb --help
for the usage.
should.be_close
, should.be_an_instance_of
, should.be_a_kind_of
,
and should.be_nil
have been deprecated. Use the dot-variants of
them. These assertions will be removed in 1.0.specrb -a
now includes -Ilib
by default for easier out-of-the-box
testing.Since version 0.3, a Gem of test/spec is available. You can install with:
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 http://chneukirchen.org/releases/gems
Version 1.0 (February 2006): first stable release.
Please mail bugs, suggestions and patches to chneukirchen@gmail.com.
Darcs repository (“darcs send” is welcome for patches):
http://chneukirchen.org/repos/testspec
Copyright (C) 2006, 2007 Christian Neukirchen
test/spec is licensed under the same terms as Ruby itself.
You can download test/spec 0.3 at:
http://chneukirchen.org/releases/test-spec-0.3.0.tar.gz
Alternatively, you can checkout from the development repository with:
darcs get http://chneukirchen.org/repos/testspec
(Patches using “darcs send” are most welcome.)
Happy hacking and have a nice day,
Christian Neukirchen
ea043ab5837994179d4659aa1e2fcc88 test-spec-0.3.0.tar.gz
d0e1c45c2e814cc26a7967232ca99f6c test-spec-0.3.0.gem
NP: The Rolling Stones—Gimmie Shelter