test/spec, a BDD interface for Test::Unit
Copyright © 2006, 2007 Christian Neukirchen <[email protected]>
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
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. test/spec has been tested successfully with FlexMock and Mocha.
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.)
test/spec adds two global methods, Object#should and Kernel.context.
You can use assert_*
freely in specify-blocks; Object#should works in plain Test::Unit test cases, too, but they will not be counted.
Wrapped assertions
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
(a
, an
and be
without arguments are optional and no-ops.)
Additional assertions
These assertions are not included in Test::Unit, but have been added to test/spec for convenience:
-
should.not.satisfy
-
should.include
-
a.should.
predicate (works likeassert a.
predicate?
) -
a.should.be
operator (where operator is one of>
,>=
,<
,<=
or===
) -
should.output
(require test/spec/should-output)
If you write an useful general-purpose assertion, I’d like to hear of it and may add it to the test/spec distribution.
Messaging/Blaming
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 message.
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 documentation (e.g. see SPECS):
== 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
Disabled specifications
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.
specrb
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
Run specrb --help
for the usage.
Installing with RubyGems
Since version 0.3, a Gem of test/spec is available. You can install with:
gem install test-spec
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
History
-
September 29th, 2006: First public release 0.1.
-
October 18th, 2006: Second public release 0.2.
-
Better, module-based implementation
-
Official support for FlexMock and Mocha
-
More robust Should#output
-
Should#operator
-
Nested contexts
-
Standalone test/spec runner, specrb
-
-
January 24th, 2007: Third public release 0.3.
-
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.
-
Added custom shoulds.
-
Added messaging/blaming.
-
Added disabling of specifications.
-
Small bug fixes.
-
Gem available.
-
Contact
Please mail bugs, suggestions and patches to <[email protected]>.
Darcs repository (“darcs send” is welcome for patches): chneukirchen.org/repos/testspec
Thanks to
-
Eero Saynatkari for writing
should.output
. -
Jean-Michel Garnier for packaging the first gem.
-
Mikko Lehtonen for testing the gem.
-
Thomas Fuchs for script.aculo.us BDD testing which convinced me.
-
Dave Astels for BDD.
-
The RSpec team for API inspiration.
-
Nathaniel Talbott for Test::Unit.
Copying
Copyright © 2006, 2007 Christian Neukirchen <purl.org/net/chneukirchen>
test/spec is licensed under the same terms as Ruby itself.
Please mail bugs, feature requests or patches to the mail addresses found above or use IRC to contact the developer.
Links
- Behavior-Driven Development
- RSpec
- script.aculo.us testing
-
<mir.aculo.us/articles/2006/08/29/bdd-style-javascript-testing>
- FlexMock
- Mocha
- Christian Neukirchen