1

I've recently started working on a automated testing project, that uses Ruby+Minitest and I wonder if I can run 1 test as many times as many input data I provide.

So, I have smth like (code is under NDA so I can't provide real examples)

def test_new_registrations
  result = process_new_entry(list_entries)
  assert(validator_method(result), result)
end

The trick is that inside process_new_entry method there's a loop I'm glad to get rid of and just run this test as many times as many entries are there on the list_entries

From TestNG in Java+Selenium I recall a concept of using a dataprovider which passes input data inside the test method one by one. Is there any chance a simmilar approach can be implemented here?

Wolffather
  • 13
  • 2
  • "run this test as many times as many entries" -> you mean, `list_entries.each do { |entry| process_new_entry(entry) }`? – Kache Jul 25 '20 at 15:14
  • @Kache well, that was my first thought. But as far as I'm aware that's a bad practice. What I meant was a construct which notices the list of input data before the test starts then pass entries to the test one by one so the loop is ommitted. Reference from Java `@Test(dataprovider = someProvider) public static void testWithParameters(Object objFrom provider) { }` – Wolffather Jul 25 '20 at 22:50
  • Oh, well Ruby's metaprogramming is very powerful, so you can do that with vanilla Ruby. You don't need a special test framework or special abstractions that does loops for you, like in Java. I'll give you an example in an answer. – Kache Jul 25 '20 at 22:57
  • @Kache thanks a lot for your answer, I appreciate it! I'm on the weekend right now but will be at my workplace in a day. Just to be clear that efforts are not in vain – Wolffather Jul 26 '20 at 08:13

1 Answers1

0

If you have:

class MyTest
  TESTCASES = {
    // input => expected
    a: "a",
    b: "b",
    c: "c",
  }

  def test_testcases
    TESTCASES.each do |entry, expected|
      result = process_one(entry)
      assert(expected, result)
    end
  end
end

And you really want to run each case in its own test instead, then it's just:

MyTest
  TESTCASES = {
    // input => expected
    a: "a",
    b: "b",
    c: "c",
  }

  TESTCASES.each do |entry, expected|
    define_method("test_testcase_#{entry}") do
      result = process_one(entry)
      assert(expected, result)
    end
  end
end

If you REALLY wanted to have the exact same syntax as in Java, it'd be possible to convert the above into a library so that this would work:

testcase_parameters(TESTCASES) # left as an exercise to the reader
def test_testcase(entry, expected)
  assert(expected, process_one(entry))
end

But I don't see the benefit of being so indirect/abstract about it. I would suggest to stay explicit, keeping the code and execution state inspect-able and easy to debug:

class MyTest
  TESTCASES = {
    // input => expected
    a: "a",
    b: "b",
    c: "c",
  }

  def test_testcases
    results = TESTCASES.map do |entry, expected|
      result = process_one(entry)
      [entry, [expected, result]]
    end

    invalid_results = results.select { |e, (ex, res)| ex != res }

    # This is a very easy-to-breakpoint place in code, to easily view all results:
    # binding.pry

    assert_empty invalid_results # failure cases will be printed out by test output
  end
end
Kache
  • 15,647
  • 12
  • 51
  • 79
  • you've really saved the day for me, thanks a lot! Your solutuion with ```invalid_results``` works perfectly! – Wolffather Jul 27 '20 at 09:34