Reputation: 181
When I decided to learn Ruby a short while ago I also decided that I'd start using unit tests and the TDD methodology. I started out using Test::Unit and wrote a couple of very small classes to get a feel for unit testing in general and Ruby and Test::Unit in particular.
So far everything has been quite easy but then I wanted to do something similar to nUnit's TestCase as I had 20 tests where the only thing that changed was the input and output.
The code looks like this:
def test_2_inserted_return_true
actual = @prime_generator.is_prime?(2)
assert_equal(true, actual)
end
def test_3_inserted_return_true
actual = @prime_generator.is_prime?(3)
assert_equal(true, actual)
end
def test_5_inserted_return_true
actual = @prime_generator.is_prime?(5)
assert_equal(true, actual)
end
Which is quite horrible from a DRY-perspective. What I would want is something similar to nUnit's TestCase.
Something like this:
[TestCase(2.5d, 2d, Result=1.25d)]
[TestCase(-2.5d, 1d, Result = -2.5d)]
public double ValidateDivision(double numerator, double denominator)
{
var myClass = new MyClass();
return myClass.Divide(numerator,denominator);
}
I've tried googling but could not find anything about Test::Unit. I've found some on RSpec and Selenium but that doesn't really help me. I also tried searching here but couldn't find anything either.
I thought about making the test method take parameters but defining methods again like that... Not happy about it. Besides, if I remember correctly it wasn't even possible (I am not able to test now or I would).
So, my question is: Is it possible to do data driven testing with Test::Unit in Ruby (1.9.2)? If not then what frameworks can do it?
Upvotes: 2
Views: 492
Reputation: 1
I did this via metaprogramming, using some samples I found on Jay Fields blog. Worked great, but the call to def_each had to be done as the first line of my test case file. Hope this example helps someone, would have loved to have seen this answer but learning about define_method was pretty cool :)
def self.def_each(method_names, &block)
method_names.each do |method_name|
method_name_fixed="test_"+method_name
define_method method_name_fixed do
instance_exec method_name, &block
end
end
end
@@servers = ["pixeltmp01.fetchback.com","pixeltmp02.fetchback.com","pixeltmp03.fetchback.com"]
# generate test methods using fixtures and any other testcase-specific data
def_each @@servers do |method_name|
@fb.withPixelServer(method_name)
self.LandingPixelM
end
Upvotes: 0
Reputation: 6585
You should check out rspec for unit testing:
require 'prime'
describe "Numbers List" do
[2, 3, 5, 7, 11, 13, 17].each do |i|
it "#{i} is prime" do
i.prime?
end
end
end
running yields:
scratch-1.9.2> rspec -cfn primes.rb
Numbers List
2 is prime
3 is prime
5 is prime
7 is prime
11 is prime
13 is prime
17 is prime
Finished in 0.00146 seconds
7 examples, 0 failures
Upvotes: 2
Reputation: 369428
Let's not forget about good ol' loops:
def test_that_the_first_few_primes_are_detected_as_prime
[2, 3, 5, 7, 11, 13, 17].each do |p|
assert @primality_tester.prime?(p)
end
end
Some people would use metaprogramming to define separate test methods, but in this case I think it's overkill:
[2, 3, 5, 7, 11, 13, 17].each do |p|
define_method :"test_that_#{p}_is_detected_as_prime" do
assert @primality_tester.prime?(p)
end
end
Generally, I don't think tests should be DRY. They should be DAMP (descriptive and meaningful phrases). After all, tests are your specification and your documentation, and they are read by people who may not be familiar with Ruby or even programming in general. So, I'm not even sure that your original example is bad, especially if you clean it up a bit like I did above:
def test_that_2_is_detected_as_prime
assert @primality_tester.prime?(2)
end
def test_that_3_is_detected_as_prime
assert @primality_tester.prime?(3)
end
def test_that_5_is_detected_as_prime
assert @primality_tester.prime?(5)
end
Here's what I did:
assert
instead of assert_equal
: Checking for equality to true
is almost never a good idea anyway, and assert
already checks that the result is truthy, so why bother?@prime_generator
to @primality_tester
, since, well, it doesn't generate primes, it only checks whether a number is prime.is_prime?
to just prime?
, since the question is already implied by the question markHowever, the best solution IMO would be something like this:
def test_that_the_first_few_primes_are_detected_as_prime
assert @primality_tester.prime?(2)
assert @primality_tester.prime?(3)
assert @primality_tester.prime?(5)
end
There's duplication in there, but it is duplication that is necessary to keep the test readable and pronouncable. DRY is a good principle for production code, which evolves and is extended, but for tests, DRY always needs to be balanced against DAMP. (Maybe it would make sense to write a custom assertion, but I doubt it. At least I can't come up with a good name for one, which is always a hint.)
A completely different approach would be property-based checking similar to Haskell's QuickCheck or .NET's Pex. There's a Ruby port of QuickCheck called RushCheck, but it has been unmaintained since 2006 and maintenance has only picked up a couple of weeks ago with still a lot of work left to do to get it up to speed on recent versions of Ruby.
Upvotes: 4