When I’m doing unit testing for code that I don’t trust very much, I’ll commonly use this pattern:
- Think up many (perhaps dozens) of expectations I have for the output of my function (I think of these as “theories” about how the code works).
- Spin up many thousands of objects.
- Run each object through the dozens of assertions I’ve coded up reflecting my expectations of how the code works.
In Ruby’s Test::Unit (to which I am new), I’ve been doing something like this:
class TestFooBarrer < Test::Unit::TestCase
def setup
@lots_of_objects_to_be_tested = FooBarrer.bar_lots_of_foos
end
def assert_foo_has_been_barred(foo)
assert_kind_of Bar, foo
end
def assert_foo_has_baz_method(foo)
assert_respond_to foo, :baz
end
#Many more assertions along those lines go here.
def test_all_theories
@lots_of_objects_to_be_tested.each do |foo|
assert_foo_has_been_barred(foo)
assert_foo_has_baz_method(foo)
# etc.
#
end
end
end
This obviously gets a bit unwieldy when the number of theories I’m testing is in the dozens, and involves what looks to me like a lot of needless repetition. I’d prefer to do something like this:
class FooTheories
def self.assert_all_theories(foo)
# ???
end
def assert_foo_has_been_barred(foo)
assert_kind_of Bar, foo
end
def assert_foo_has_baz_method(foo)
assert_respond_to foo, :baz
end
#Many more assertions along those lines go here.
end
class TestFooBarrer < Test::Unit::TestCase
def setup
@lots_of_objects_to_be_tested = FooBarrer.bar_lots_of_foos
end
def test_all_theories
@lots_of_objects_to_be_tested.each do |foo|
FooTheories.assert_all_theories(foo)
end
end
end
Basically, I’m looking for a way to write a bunch of assertions in one place and then call them over and over again on a large quantity of objects.
Is there any support for something like that in Ruby? I’m not tied to Test::Unit particularly. Any of the testing frameworks is fine.
What I would do is generate tests on the fly. Add a method in your test_helper:
And then you can make your test suite like the following
And then if it fails, you’ll get to know which object failed because the name of the test is the string representation of the object. Ex message: