Whettingstone
Whettingstone

Reputation: 181

Equivalent to nUnit's TestCase in Ruby 1.9.2 Test::Unit

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

Answers (3)

chollaball
chollaball

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

Wes
Wes

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

Jörg W Mittag
Jörg W Mittag

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:

  • Rename the tests: Now, the test name forms a complete sentence, and it doesn't simply restate what the test does.
  • Use 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?
  • Rename @prime_generator to @primality_tester, since, well, it doesn't generate primes, it only checks whether a number is prime.
  • Rename is_prime? to just prime?, since the question is already implied by the question mark
  • Last, but certainly not least: Fix the formatting, since not only was the formatting inconsistent with standard Ruby coding conventions, it wasn't even consistent within itself

However, 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

Related Questions