DDT
Data driven testing for Ruby with RSpec. DDT kills bugs.
DDT is a plugin for RSpec that makes data-driven testing easy. It allows you to define a YAML file for a specific method that includes test input and expected outputs. See below for an example.
License
DDT is released under the MIT license. See the LICENSE file.
Requirements
DDT requires RSpec because, well, it's an RSpec plugin. In the future, I may make DDT more general and let the RSpec part be optional.
DDT should work on any Ruby. It's only been tested on Ruby 1.9.3 so far, though.
Installation
If your system is set up to allow it, you can just do
gem install ddt
Or, if you prefer a more hands-on approach or want to hack at the source:
git clone git://github.com/simplifi/ddt.git
cd ddt
rake install
If you are working on a system where you need to sudo gem install
you can do
rake gem
sudo gem install ddt
As always, you can rake -T
to find out what other rake tasks we have
provided.
Basic Usage
Right now, DDT::TruthTesting
that is the main workhorse of DDT.
DDT::TruthTesting
adds some methods to RSpec to enable a truth
testing. DDT::TruthTest
automates the process of creating an
object, calling a method on it with some specified input, and checking
the state of the object afterwords against what is expected, i.e., the
"truth". DDT::TruthTest
defines a class called Truth
for the
class under test; the Truth
class encapsulates the input and
expected output.
This example and the others below are taken from spec/truth_test/truth_test_spec.rb
First, include ddt in your test suite. Probably this just means
adding require 'ddt'
to your spec_helper.rb
Suppose we have a classed called Cat
that has a state called
disposition
and a method called pet
:
class Cat
attr_accessor :disposition
def initialize
@disposition = "mrow."
end
# cats are temperamental. my cat does not
# like to be petted on the belly, but she
# won't let you know until afterwords.
def pet how
predisposition = @disposition
if how =~ /belly/
@disposition = "*hiss*"
end
predisposition
end
end
We can write a truth testing spec file for Cat#pet
like this:
describe Cat do
truth_test :pet
end
When we run rspec, DDT will look for a file called
truths/cat/pet_truths.yaml
containing YAML test cases. Here's an
example:
---
input: "on the head"
output: "mrow."
disposition: "mrow."
---
input: "on the belly"
output: "mrow."
disposition: "*hiss*"
For each of these test cases, DDT::TruthTest
will create a new Cat
instance and call the pet
method with "input" as the argument. It
then does an RSpec example that looks something like
cat.pet("on the head").should == "mrow."
as well as
cat.disposition.should == "mrow."
Mapping test data
DDT::TruthTest
lets us map test data using
Object::define_truth
, which takes as arguments an instance of the
object under test and a hash corresponding to the YAML test case. As
an example, consider the Dog
class here:
class Dog
attr_accessor :name, :weight, :age
def parse! str
d = str.split(",")
@name = d[0]
@weight = d[1].to_f
@age = d[2].to_i
self
end
end
# example:
rover = Dog.new.parse! "rover", 31.5, 5
rover.name # => "rover"
rover.weight # => 31.5
rover.age # => 5
In our test data, the weight needs to be converted to a float and the age needs to be converted to an int, so we'll manually add some conversions to our truth class:
Dog::define_truth do |dog, data|
# make age an integer
dog.age = data["age"].to_i
# make weight a float
dog.weight = data["weight"].to_f
end
We can then define truths for Dog#parse!
in
truths/dog/parse\!_truths.yaml
(note the \!
):
---
input: "Fido, 30.0, 3"
name: "Fido"
weight: 30.0
age: 3
And test in RSpec:
describe Dog do
truth_test :parse!
end
Note, in the Cat
example above, Cat::define_truth
is called
automatically by truth_test
.
Specifying the test instance
The Truth
class also provides a method called Truth#tester
that
supplies the instance to test. By default, Object#new
is called
with no arguments to supply the instance. This method can be overridden to
specify other arguments:
class Wolf
attr_accessor :call
def initialize call
@call = call
end
end
# we have to first create the truth class
Wolf::define_truth
# then we can monkey patch the tester method
class Wolf::Truth
def tester
self.class.tester || Wolf.new("AOOOO")
end
end
It's also possible to use RSpec's stubbing to do this:
Wolf::Truth.should_receive(:tester).and_return(Wolf.new "draw blood")
Contributing
The usual github process applies here:
- Fork it
- Create your feature branch (
git checkout -b my-new-feature
) - Commit your changes (
git commit -am 'Added some feature'
) - Push to the branch (
git push origin my-new-feature
) - Create new Pull Request
You can also contribute to the author's ego by letting him know that you find String Eater useful ;)