Unit Testing - some basic questions

It’s taken a little too long to see the light for unit testing, but the
time has come! I realize that this isn’t the most exciting subject for
most developers, but hopefully a couple of you have some sage advice.

I’ve been trying out some test cases, and have focused on a single model
trying to figure it all out before moving to the next one.

The table / model I am working with is log_edits:
id (int)
employee_id (int)
edit_time (datetime)
edit_position (varchar 10)
original_value (float 8)
new_value (float 8)
source_table (varchar 50)

My model file log_edit.rb:
class LogEdit < ActiveRecord::Base
validates_presence_of :employee_id, :edit_time, :edit_position
validates_presence_of :original_value, :new_value :source_table
validates_numericality_of :employee_id, :original_value, :new_value
end

Is there a way to verify if the following is true within the model?
employee_id is an integer
orig_value and new_value are either whole or half numbers eg 12.0 or 3.5
and they are less then 50 but >= 0?
(a reg ex for .0 or .5 might work)

I’ve written up a small set of test cases to check it out, but its not
working quite how I expected. Here is a snipped of what is failing.

require File.dirname(FILE) + ‘/…/test_helper’

require ‘test/unit’
require ‘log_edit’

class LogEditTest < Test::Unit::TestCase
fixtures :log_edits
def test_invalid_with_empty_attributes
edits_time = LogEdit.new
assert !edits_time.valid?
# value not set. should be invalid and therefore pass
assert edits_time.errors.invalid?(:employee_id)

# Set the value and it should fail since it is valid,
# but no failure appears when run!!
edits_time.employee_id = 123
assert edits_time.errors.invalid?(:employee_id)

end
end

Is there a way to verify if the following is true within the model?

validates_numericality_of :employee_id, :original_value, :new_value
:only_integer
=> true

and they are less then 50 but >= 0?

validates_inclusion_of :original_value, new_value :in => 0…50

orig_value and new_value are either whole or half numbers eg 12.0 or 3.5

def validate

do you custom validation here

end

# Set the value and it should fail since it is valid,
# but no failure appears when run!!
edits_time.employee_id = 123
assert edits_time.errors.invalid?(:employee_id)

I believe this is going to pass because you did not ask to validate
the object again, so you errors has not changed from the original
invalid state.

edits_time.employee_id = 123
assert edits_time.valid?
assert edits_time.errors.invalid?(:employee_id)

the last assertion here should fail because the errors has been
updated by the previous line.

On May 7, 4:56 pm, Bob B. [email protected]

Thanks for the info, a lot of it makes sense.

Two quick follow ups.

in def validate, can I have multiple validates, ie one for the value
fields, and then a second validate function for say source_table? Would
it require separate validates ie def validate_value, def
validate_source_table

How do I call the validate within the model, or is it automatically run?

Second was about the failure of the failure. So if I understand this
correctly with my original code:

edits_time = LogEdit.new
assert !edits_time.valid?
# value not set. should be invalid and therefore pass
assert edits_time.errors.invalid?(:employee_id)

Robert W. wrote:

I believe this is going to pass because you did not ask to validate
the object again, so you errors has not changed from the original
invalid state.

edits_time.employee_id = 123
assert edits_time.valid?
assert edits_time.errors.invalid?(:employee_id)

the last assertion here should fail because the errors has been
updated by the previous line.

On May 7, 4:56 pm, Bob B. [email protected]

ar_object.valid?

*Crud, tab + space = not my friend. Sorry for the double post.

Thanks for the info, a lot of it makes sense.

Two quick follow ups.

in def validate, can I have multiple validates, ie one for the value
fields, and then a second validate function for say source_table? Would
it require separate validates ie def validate_value, def
validate_source_table

How do I call the validate within the model, or is it automatically run?

Second was about the failure of the failure. You said that I did not ask
to validate the object again. Could you explain that in more detail?
How do I force an object to be validated multiple times within a single
test? ie If I had a test to check all boundary conditions of 1
parameter I’d like it to evaluate it each time it is changed.

Bob

Steve R. wrote:

ar_object.valid?

Dang, as simple as that huh? Added it right after the assignment and it
threw the failure right where it should have. sweet!

in def validate, can I have multiple validates, ie one for the value
fields, and then a second validate function for say source_table? Would
it require separate validates ie def validate_value, def
validate_source_table

An example is worth a thousand words:

class Person < ActiveRecord::Base
protected
def validate
errors.add_on_empty %w( first_name last_name )
errors.add(“phone_number”, “has invalid format”) unless
phone_number =~ /[0-9]*/
end

  def validate_on_create # is only run the first time a new object

is saved
unless valid_discount?(membership_discount)
errors.add(“membership_discount”, “has expired”)
end
end

  def validate_on_update
    errors.add_to_base("No changes have occurred") if

unchanged_attributes?
end
end

How do I call the validate within the model, or is it automatically run?

Rails will take care of calling validation at the appropriate time.

Second was about the failure of the failure. You said that I did not ask
to validate the object again. Could you explain that in more detail?
How do I force an object to be validated multiple times within a single
test? ie If I had a test to check all boundary conditions of 1
parameter I’d like it to evaluate it each time it is changed.

Sorry I should have been more clear. s.ross answered this tersely,
yet correctly.

edits_time.valid?

or

assert edits_time.valid? # if you want to test the changed state

On May 7, 6:10 pm, Bob B. [email protected]

Robert W. wrote:

An example is worth a thousand words:

It sure is, and it helps a lot. Was checking out the api on errors to
fill in the blanks, but I didn’t see anything about the %w in there. I
thought it might be a regular expression for word (only allow letters
and numbers) but when I added it to the model it didn’t work. ie throw
an error if it is blank, or contains bad input.
[ errors.add_on_empty %w( source_table ) ]

edits_time.source_table = ‘[email protected]]' # also tried '[email protected]]’
edits_time.valid?
assert edits_time.errors.invalid?(:source_table)

it winds up failing, meaning the ‘`[email protected]]’ is a valid value for source
table.

    errors.add_on_empty %w( first_name last_name )

Rails will take care of calling validation at the appropriate time.
Cool, good to know.

Sorry I should have been more clear. s.ross answered this tersely,
yet correctly.

yep. I didn’t realize that .valid? caused the object to reevaluate
itself, figured it would have the same problem that the invalid() call
would. Now I know, and knowing if half the battle =)

Bob B. wrote:

Robert W. wrote:

An example is worth a thousand words:

It sure is, and it helps a lot. Was checking out the api on errors to
fill in the blanks, but I didn’t see anything about the %w in there. I
thought it might be a regular expression for word (only allow letters
and numbers) but when I added it to the model it didn’t work. ie throw
an error if it is blank, or contains bad input.
[ errors.add_on_empty %w( source_table ) ]

edits_time.source_table = ‘[email protected]]' # also tried '[email protected]]’
edits_time.valid?
assert edits_time.errors.invalid?(:source_table)

it winds up failing, meaning the ‘`[email protected]]’ is a valid value for source
table.

    errors.add_on_empty %w( first_name last_name )

Rails will take care of calling validation at the appropriate time.
Cool, good to know.

Sorry I should have been more clear. s.ross answered this tersely,
yet correctly.

yep. I didn’t realize that .valid? caused the object to reevaluate
itself, figured it would have the same problem that the invalid() call
would. Now I know, and knowing if half the battle =)

%w is just straight-ahead ruby. It means “turn the string that follows
into an array”. It breaks the string up at the spaces. Just a quick
and easy way to initialize an array of words.
my_array = %w(four score and seven)
my_array gets the value [“four”, “score”, “and”, “seven”]

jp

Bob B. wrote:
[ errors.add_on_empty %w( source_table ) ]

Jeff P. wrote:

%w is just straight-ahead ruby. It means “turn the string that follows
into an array”. It breaks the string up at the spaces. Just a quick
and easy way to initialize an array of words.
my_array = %w(four score and seven)
my_array gets the value [“four”, “score”, “and”, “seven”]

jp

Well that explains why it wasn’t doing anything since it was just a 1
element array.

As I’ve been writing up some test cases, I was wondering what some of
the best practices for Testing are - and Google has not been very
helpful.

I am trying to automate the writing of these tests as much as possible
and have gotten it somewhat condensed, but was wondering if further
improvements are possible. Right now I am testing the models, so I
create a valid object in the setup function, then I test that a
blank/new object is invalid, and that the object from setup is valid.

Next I test each parameter’s corner cases like so - this test is for the
hours in a day, in half hour increments, 0-23.5

def test_log_edit_value_corner_cases
#test new_v and original_value together since they accept the same
values.
failure_cases = [25, 24, -1, 0.25, 23.75, ‘b’]
success_cases = [0, 0.5, 1, 17, 17.5, 23, 23.5]
#start with a valid object, then just test that
#it is (in)valid by changing just these fields
failure_cases.each do |f_case|
@valid_obj.new_value = f_case
@valid_obj.original_value = f_case
@valid_obj.valid?
assert @valid_obj.errors.invalid?(:new_value)
assert @valid_obj.errors.invalid?(:original_value)
assert !@valid_obj.valid?
end

#Don't test the fields specifically.
#If anything is bad, the whole thing will be.
success_cases.each do |s_case|
  @valid_obj.new_value = s_case
  @valid_obj.original_value = s_case
  assert @valid_obj.valid?
end

end

Here you can see that all I have to do is add new values to the array to
change my cases. pretty simple. But each and every attribute will have
these same two loops/function, with just the attributes and the values
of these arrays changed.

I was wondering if you can have non test functions within a test file,
and if they be called and have stuff returned, or if I could create some
sort of generic function in a seperate file and have the test files
include it.

Just because something may be possible, it doesn’t mean its a good idea.
I didn’t know if either way was generally accepted and was wondering
what others thought.

Thanks for any advice.

This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.

| Privacy Policy | Terms of Service | Remote Ruby Jobs