Frank Koehl Frank Koehl - 5 months ago 11
Ruby Question

Data providers for Rails testing

New to both Ruby and Rails, working on an app using

Test::Unit
. I come from a background in PHP and PHPUnit, where they provide formal structure for
data providers
: you define a nested array/hash of arguments in one method, then loop a single test through it, swapping the arguments on each pass. It's a concise way to repeatedly test the the same event with different parameters.

The only solution I have found thus far is to replicate the concept manually, inside a single test:

test "should properly do something" do
provider = [
{:var1 => 'foo', :var2 => 'bar', :expected => true},
{:var1 => 'foo', :var2 => 'baz', :expected => true},
{:var1 => 'invalid', :var2 => 'bar', :expected => false},
# ...
]
provider.each do |t|
assert_equal(t.expected, SomeObject.some_method(t.var1, t.var2))
end
end


This doesn't give me as much useful test output, since it can't discern which loop actually fails in the event of an error.

Is there a better way to do this?

Answer

You could just reformat your test code like so:

[
  ['foo',     'bar', true ],
  ['foo',     'baz', true ],
  ['invalid', 'bar', false],
  # …
].each do |v1, v2, expected|
  test "expecting #{expected} with #{v1} and #{v2}" do
    assert_equal(expected, SomeObject.some_method(v1, v2))
  end
end

However, with Rails I find a combination of RSpec, FactoryGirl and Faker/ffaker easier to use. I also don't create a “data provider” and test against this matrix, but specify the behaviour (whether it is Test::Unit or RSpec), like so:

test "expecting positive result" do
  assert SomeObject.some_method('foo', 'bar')
  assert SomeObject.some_method('foo', 'baz')
  # …
end

test "expecting negative result" do
  assert_false SomeObject.some_method('invalid', 'baz')
  # …
end

or (RSpec):

require 'spec_helper'

describe SomeObject do
  describe '#some_method' do
    specify "positive result" do
      expect(SomeObject.some_method 'foo', 'bar').to be true
      expect(SomeObject.some_method 'foo', 'baz').to be true
      # …
    end

    specify "negative result" do
      expect(SomeObject.some_method 'invalid', 'bar').to be false
      # …
    end
  end
end