# Test/spec 0.10, a BDD interface for Test::Unit

**URL:** <https://www.ruby-forum.com/t/test-spec-0-10-a-bdd-interface-for-test-unit/159147>\
**Category:** Ruby\
**Created:** [February 1, 2009, 12:30pm UTC](https://www.ruby-forum.com/t/test-spec-0-10-a-bdd-interface-for-test-unit/159147 "2009-02-01T12:30:38Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![cneuhaus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/34f0e0/32.png) [@cneuhaus](https://www.ruby-forum.com/u/cneuhaus)\
**Post date:** [February 1, 2009, 12:30pm UTC](https://www.ruby-forum.com/t/test-spec-0-10-a-bdd-interface-for-test-unit/159147/1 "2009-02-01T12:30:38Z")

</div>

Hello,

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  
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.)

== 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

== Additional assertions

These assertions are not included in Test::Unit, but have been added  
to test/spec for convenience:

- should.not.satisfy
- a.should. (works like assert a.?)
- a.should.be (where is \<, \<=, \>, \>=, or ===)
- should.output, to check what is printed

== 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:

```
== 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](http://script.aculo.us) BDD testing which convinced me.
- Dave A. for BDD.
- The RSpec team for API inspiration.
- Nathaniel T. for Test::Unit.

== Copying

Copyright (C) 2006, 2007, 2008, 2009 Christian N.  
[http://purl.org/net/chneukirchen](http://purl.org/net/chneukirchen)  
test/spec is licensed under the same terms as Ruby itself.

== Where can I get it?

You can download test/spec 0.10 at

```
    http://chneukirchen.org/releases/test-spec-0.10.0.tar.gz

```

Alternatively, you can checkout from the development repository with:

```
       darcs get http://chneukirchen.org/repos/testspec

```

Please mail bugs, suggestions and patches to  
[mailto:removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid).

(Patches using “darcs send” are most welcome.)

== Installing with RubyGems

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/](http://chneukirchen.org/releases/gems/)

== Links

Behavior-Driven Development:: [http://behaviour-driven.org/](http://behaviour-driven.org/)  
RSpec:: [http://rspec.rubyforge.org/](http://rspec.rubyforge.org/)  
[script.aculo.us](http://script.aculo.us) testing::  
[http://mir.aculo.us/articles/2006/08/29/bdd-style-javascript-testing](http://mir.aculo.us/articles/2006/08/29/bdd-style-javascript-testing)  
FlexMock:: [http://onestepback.org/software/flexmock/](http://onestepback.org/software/flexmock/)  
Mocha:: [http://mocha.rubyforge.org/](http://mocha.rubyforge.org/)

Happy hacking and have a nice day,  
Christian N.

9519c8f4683268bb47c0fc6a3d6fe925d29fa52a test-spec-0.10.0.tar.gz  
ec7ea48707f247316a4556c3790d49e580c2e8c9 test-spec-0.10.0.gem

---

<div class="post-metadata">

**Author:** ![cneuhaus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/34f0e0/32.png) [@cneuhaus](https://www.ruby-forum.com/u/cneuhaus)\
**Post date:** [February 1, 2009, 1:45pm UTC](https://www.ruby-forum.com/t/test-spec-0-10-a-bdd-interface-for-test-unit/159147/2 "2009-02-01T13:45:48Z")

</div>

Christian N. wrote:

> In test/spec, it looks like this:
> 
> ```
> require 'test/spec'
> 
> context "Foo" do
> specify "should bar" do
> (2 + 3).should.equal 5
> end
> end
> 
> ```

We use TextMate at work. (This may come as a surprise to those of you  
who read  
my posts, but I lack the political prowess to help us move to Aptana or  
something with a better GUI…)

When we put a cursor inside a def test\_case and claw \<Shift+Splat+R\>,  
TextMate  
finds the ‘def test\_case’ above us, and runs ruby -n  
file\_name/test\_case.

That gives us Incremental Testing during our Test-Driven Development  
sessions.

We auditioned test/spec, and we have many specs still in producton, but  
we found  
that \<Shift+Splat+R\> did not always work. Sometimes it runs the right  
spec, and  
sometimes the wrong one…

I’m aware this is not your fault, but I remain curious about our  
editors’  
general abilities to help and not hinder TDD!

Now a question about spec architecture in general.

> ```
> specify "should have to_s"
> 42.should equal_string("42")
> end
> end
> 
> ```

As far as I can tell, the only aspect of any \*spec system that is not  
just a  
remap of test\_case architecture is the ability to run nested context{}  
blocks  
with incrementally nested setup{} blocks.

context ‘foo’ do  
setup{ @foo = Foo.new }  
it(‘should see foo’){ @foo.should.not.be.nil }

```
 def increment_foo
   return @foo.x + 42
 end

 context 'foo' do
   setup{ @bar = Bar.new }
   it 'should see both foo & bar' do
     @foo.should.not.be.nil
     @bar.should.not.be.nil
   end
 end

```

end

That is very important for projects with lots of business rules.

However, can the inner context see the method increment\_foo() ?

---

<div class="post-metadata">

**Author:** ![cneuhaus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/34f0e0/32.png) [@cneuhaus](https://www.ruby-forum.com/u/cneuhaus)\
**Post date:** [February 1, 2009, 5:42pm UTC](https://www.ruby-forum.com/t/test-spec-0-10-a-bdd-interface-for-test-unit/159147/3 "2009-02-01T17:42:13Z")

</div>

On Feb 1, 2009, at 6:44 AM, Phlip wrote:

> We use TextMate at work.

> When we put a cursor inside a def test\_case and claw \<Shift+Splat  
> +R\>, TextMate finds the ‘def test\_case’ above us, and runs ruby -n  
> file\_name/test\_case.
> 
> That gives us Incremental Testing during our Test-Driven Development  
> sessions.
> 
> We auditioned test/spec, and we have many specs still in producton,  
> but we found that \<Shift+Splat+R\> did not always work. Sometimes it  
> runs the right spec, and sometimes the wrong one…

Here are the regular expressions we use inside TextMate to find specs  
from test/spec:

```
 # test/spec.
 spec = $3 || $4 if lines.find { |line| line =~ /^\s*(specify|

```

it)\s+(’(._)’|"(._)")+\s\*({|do)/ }  
context = $3 || $4 if lines.find { |line| line =~ /^\s\*(context|  
describe)\s+(’(._)’|"(._)")+\s\*({|do)/ }

If those need tweaking, let us know.

James Edward G. II

---

<div class="post-metadata">

**Author:** ![cneuhaus](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/c/34f0e0/32.png) [@cneuhaus](https://www.ruby-forum.com/u/cneuhaus)\
**Post date:** [February 1, 2009, 9:50pm UTC](https://www.ruby-forum.com/t/test-spec-0-10-a-bdd-interface-for-test-unit/159147/4 "2009-02-01T21:50:59Z")

</div>

> ```
> context = $3 || $4 if lines.find { |line| line =~ /^\s*(context| 
> 
> ```
> 
> describe)\s+(’(._)’|"(._)")+\s\*({|do)/ }
> 
> If those need tweaking, let us know.
> 
> James Edward G. II

Thanks! I forwarded them to The Usual Suspects!
