Unit-testing – How to test the validation without being too much perfectionist, yet to leave most part of logic tested

unit testing

For example I would like to validate that name is letters only and is between 4 and 14 letters length. I have the following code in model:

validates: name, :format => { :with => /^[a-zA-Z]+$/,
                              :message => 'Name should be letters only. },
                 :length => { :minimum => 4, :maximum => 14 }

So it clearly lets me do what I want.

But as for unit tests, I have a bit too much perfectionism so I set something like

invalid_names = ['1234',
                 'qwe',
                 '1%#$#$',
                 'Sam1',
                 '%',
                 random_string(15)] #I also have a test method to create random string with parametrized length

valid_names = %w['test',
                 'Travis',
                 'John',
                 random_string(5),
                 random_string(14),
                 random_string(4)]

and test each of them in a loop with asserts, like

invalid_names.each do |name|
    user = User.new(:name => name)
    user.save
    assert user.errors[:name].any?, "#{name} is valid."
end

So it definitely works great. But it is too verbose (because of valid/invalid names arrays, and added random_string method), also I can't be sure my test actually tests all symbols and their combinations possible and all lengths and stuff, even though I am definitely sure it works as expected.

So what is acceptable way to test my validation without being too much perfectionist, yet to leave most part of logic tested?

Am I just set in a mind trick of trying to write a perfect code just to write a perfect code and forgetting about main finish goal: working product?

Best Answer

Some suggestions:

  • Avoid to use a random generator directly inside unit tests if there is no compelling reason for it. This has a high risk of producing non-reproducible cases. If you want to use a random string, choose the string randomly once and hardcode that choosen string into your test.

  • An old wisdom of testing is that you cannot test every possible combination of input data if the allowed range of input values is too huge. Choosing good test cases is brainwork, the idea is to use as few test cases as needed to test the largest possible number of failure cases.

For example, classify your input domain into categories:

  • string with length 0
  • strings with length between 0 and 4
  • strings with length 4
  • strings with length between 4 and 14
  • strings with length greater 14

And as a second classification

  • strings with no legal characters
  • strings with some legal characters
  • string with only legal characters

Now, make sure you have at least one test case for each category. Of course, this example shows only finding tests by using "equivalence classes". Other techniques are designing your tests for edge cases (like the ones in this answer), or tests for getting full code coverage and branch coverage.

Related Topic