Refactoring Of Simple Model Test For Rails
Join the DZone community and get the full member experience.
Join For Free
# =============================================================================
# Purpose: Refactored TestCase for simple Rails ActiveRecord::Base and
# ActiveForm classes.
# Author: Manuel Holtgrewe
# License: Public Domain
#
# Feel free to flesh out the tests further and make it a more poweful library.
# Drop me a line if you find the module useful or if you created something more
# powerful on top of it.
# =============================================================================
#
# Include this module in your test classes. Then, you define some valid data,
# the model class to test and some "patches" to the valid data to make it invalid.
#
# Sure, this will only work for very simple models but you want to spend as few
# time on those as possible, right?
#
# Assuming you have the following model:
#
# class MyModel < ActiveRecord::Base
# validates_length_of :title, :in => 10..100
# validates_numericality_of :number
# end
#
# You can then write the following TestCase:
#
# class MyModelTest < Test::Unit::TestCase
# include SimpleMethodTests # The basic tests are defined in this module.
#
# fixtures :my_models
#
# def setup
# @model_class = MyModel
# @valid_data = { :title => 'Abracabra!', :number => 10 }
# @invalid_patches =
# [
# [ :title, nil ], [ :title, 'a' * 9 ], [ :title, 'a' * 101 ],
# [ :number, nil ], [ :number, 'a' ], [ :title, '123a' ],
# ]
# end
#
# def test_a_nonbasic_test
# flunk, "Wheee!"
# end
#
# # Overwrite a test from the mixin.
# def test_valid_should_return_false_and_add_errors_with_invalid_data
# assert true, "Removing this test!"
# end
# end
#
# You can both test ActiveRecord::Base and ActiveForm classes (CRUD is not tested
# on ActiveForm classes). If you test ActiveRecord::Base classes then you have to
# provide at least one fixture.
module SimpleModelTests
def test_valid_should_return_true_with_valid_data
# Do not use Model.new(attributes) since some might be marked as protected.
model = @model_class.new
@valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }
assert model.valid?
assert_equal 0, model.errors.count
end
def test_valid_should_return_false_and_add_errors_with_invalid_data
@invalid_patches.each do |key, value|
invalid_data = @valid_data.dup
invalid_data[key] = value
# Do not use Model.new(attributes) since some might be marked as protected.
model = @model_class.new
invalid_data.each { |invalid_key, invalid_value| model.send("#{invalid_key}=".to_sym, invalid_value) }
assert !model.valid?, "invalid_data[#{key.inspect}] == #{value.inspect}, errors: #{model.errors.full_messages.join('; ')}"
# If the property we set was a foreign key named NAME_id then the error
# gets added to the property NAME of the object if the object is an
# ActiveRecord::Base object.
if key.to_s =~ /^(.*)_id$/ and model.respond_to?($1.to_sym) then
assert_not_nil model.errors.on($1.to_sym), "invalid_data[#{$1.to_sym.inspect}] == #{value.inspect}"
else
assert_not_nil model.errors.on(key), "invalid_data[#{key.inspect}] == #{value.inspect}"
end
end
end
def test_create_should_work_correctly
# We only want to test this if we test an ActiveRecord::Base class.
return if not @model_class.new.respond_to?(:save)
old_count = @model_class.count
# Do not use Model.new(attributes) since some might be marked as protected.
model = @model_class.new
@valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }
assert model.save, "errors: #{model.errors.full_messages.join('; ')}"
assert model.reload
assert_equal old_count+1, @model_class.count
end
def test_update_should_work_correctly
# We only want to test update if the model objects have a method "save".
return if not @model_class.new.respond_to?(:update)
# We assume that there is at least one valid record in the database with
# different data than the @valid_data you specified above.
#
# We order by id to make the result predictable.
model = @model_class.find(:first, :order => 'id ASC')
@valid_data.each { |key, value| model.send("#{key}=".to_sym, value) }
assert model.save, "errors: #{model.errors.full_messages.join('; ')}"
assert model.reload
@valid_data.each do |key, value|
case value
when Float then
assert_in_delta value, model.send(key), 0.01
else
assert_equal value, model.send(key)
end
end
end
def test_destroy_should_work_correctly
# We only want to test this if we test an ActiveRecord::Base class.
return if not @model_class.new.respond_to?(:destroy)
old_count = @model_class.count
model = @model_class.find(:first)
id = model.id
model.destroy
assert_raises(ActiveRecord::RecordNotFound) { @model_class.find(id) }
assert_equal old_count-1, @model_class.count
end
end
Testing
Opinions expressed by DZone contributors are their own.
Comments