Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using Ruby and Minitest, how do I run the same testcase with different data, controlled only by a list

Tags:

I have Ruby 2.0 code that operates on phone numbers, which I want to test using MiniTest. I have a function which takes a phone number arguments and tests it (including asserts). Each time I call that function, I want it to be a new test case. Something like this:

listOfPhoneNumbersForTesting.each { |phone|   testphone phone }   

What I DO NOT want is this:

class test2125551212 < MiniTest::Unit::TestCase     def t2125551212          testphone "2125551212"     end end 

... repeated 10, 20, or 100 times to test each phone number ...

Obviously, I could put the loop code within a MiniTest::Unit::TestCase, but that results in just one testcase no matter how many phone numbers I test, and I don't like that. (Also, if one of the asserts fail, then no more phone numbers are tested, and I don't want that!) Also the second form looks like a violation of DRY to me, since the class name, function name, and argument all contain the phone number.

Somehow I feel that I should be able to have one class called TestPhone, and create it with the phone number argument, and get that into the MiniTest framework. but I would be willing to use setup(), Fixtures, metaprogramming, or just about anything else, if it would work.

listOfPhoneNumbersForTesting.each { |phone|   TestPhone.new phone } 

Where TestPhone is a TestCase subclass, and ends up calling testphone to do the work.

Basically, what I want is this: 1. One list of phone numbers, and if I add a number to the list, I get one more TestCase in reporting output. 2. If the tests associated with one phone number fail, the rest are still tested. 3. All phone numbers get the same testing, which includes several assertions.

Thanks very much!

like image 357
user2773661 Avatar asked Sep 12 '13 17:09

user2773661


People also ask

How to run rails test cases?

We can run all of our tests at once by using the bin/rails test command. Or we can run a single test file by passing the bin/rails test command the filename containing the test cases. This will run all test methods from the test case.

How do you run a Minitest in rails?

To run a Minitest test, the only setup you really need is to require the autorun file at the beginning of a test file: require 'minitest/autorun' . This is good if you'd like to keep the code small. A better way to get started with Minitest is to have Bundler create a template project for you.

What type of test is not typical in a Rails application?

Integration. Don't try to test all the combinations in integration tests. That's what unit tests are for. Just test happy-paths or most common cases.


1 Answers

You could dynamically define the methods.

In the following example, 6 tests are dynamically created (2 tests for each of the 3 values being tested). This means that if anything fails, the other tests still run.

require "minitest/autorun" class MyTests < MiniTest::Unit::TestCase     ['0', '1111111', '2222222'].each do |phone_number|         define_method("test_#{phone_number}_has_7_characters") do             assert_equal(7, phone_number.length)         end          define_method("test_#{phone_number}_starts_with_1") do             assert_equal('1', phone_number[0])         end     end end 

The apply test case gives the following results:

# Running tests:  F..F.F  Finished tests in 0.044004s, 136.3512 tests/s, 136.3512 assertions/s.    1) Failure: test_0_starts_with_1(MyTests) [stuff.rb:13]: Expected: "1"   Actual: "0"    2) Failure: test_0_has_7_characters(MyTests) [stuff.rb:9]: Expected: 7   Actual: 1    3) Failure: test_2222222_starts_with_1(MyTests) [stuff.rb:13]: Expected: "1"   Actual: "2"  6 tests, 6 assertions, 3 failures, 0 errors, 0 skips 

Applying the same concept to your tests, I think you want:

class MyTests < MiniTest::Unit::TestCase     listOfPhoneNumbersForTesting.each do |phone|         define_method("test_#{phone}") do             TestPhone.new phone         end     end end 

A similar approach can be taken when using the spec-style tests:

require 'minitest/spec' require 'minitest/autorun'  describe "my tests" do   ['0', '1111111', '2222222'].each do |phone_number|     it "#{phone_number} has 7 characters" do       assert_equal(7, phone_number.length)     end      it "#{phone_number} starts with 1" do       assert_equal('1', phone_number[0])     end   end end 

IMPORTANT: One thing to note is that you need to make sure that the name of the test methods created are unique for each test case.

For example, if you do not put the phone number in the method name, you end up overwriting your previously defined methods. Ultimately this means that only the last phone number gets tested.

This is because MiniTest generates the test methods on the fly and will overwrite already generated test methods, ultimately only using the last .each variable.

like image 79
Justin Ko Avatar answered Oct 03 '22 21:10

Justin Ko