Sprytne testowanie modelu w Ruby on Rails
Załóżmy, że mamy jakiś model w Ruby on Rails, np. User, który składa się z takich pól jak name, middlename, surname, sex oraz birthday. Oczywiście trzeba także dopisać jakaś walidację tego modelu. Wówczas nasz model będzie wyglądać następująco:
[ruby]
class User < ActiveRecord::Base
validates_presence_of :name, :middlename, :surname, :sex, :birthday
end
[/ruby]
Zakładam, że jestem uczciwy względem siebie i chce przetestować, czy walidacje działają poprawnie. Zaczynam więc pisać test:
[ruby]
class UserTest < Test::Unit::TestCase
fixtures :users
def test_validation_name
u = users('user1')
u.name = ''
assert !u.valid?
assert u.errors.invalid?("name")
u.name = nil
assert !u.valid?
assert u.errors.invalid?("name")
end
# ...i tak dalej... nudny w sumie kod....
end
[/ruby]
Ale przecież możemy skorzystać z dynamicznej natury Ruby'iego i wstrzyknąć wygenerowane wcześniej metody. Poniżej widać, jak to zrobić:
[ruby]
class UserTest < Test::Unit::TestCase
fixtures :users
#tworzę tablicę z poniższych elementów i iteruję po niej
%w(name middlename surname sex birthday).each do |name|
#definiuję nową metodę do testowania poprawności walidacji
define_method(:”test_validation_#{name}”) do
u = users(‘user1′)
#dynamiczne wywołanie metody, a dokładnie przypisanie pustego napisu do pola
u.send :”#{name}=”, ”
assert !u.valid?
assert u.errors.invalid?(:”#{name}”)
u.send :”#{name}=”, nil
assert !u.valid?
assert u.errors.invalid?(:”#{name}”)
end
end
end
[/ruby]
Piękne rozwiązanie, prawda?

Nazywam się
Wiktor Gworek
i jestem gospodarzem tego bloga.
No niezle, ale mozna lepiej:
Zróbmy uniwersalną metodę do, testowania validates_presence_of we wszystkich modelach jakie kiedys napiszemy (Najlepiej włożyć do modułu):
def self.test_validates_presence_of(*args)
args.each do |name|
class_eval(”
def test_validation_#{name}
u = users(‘user1′)
u.send :”#{name}=”, ”
assert !u.valid?
assert u.errors.invalid?(:”#{name}”)
u.send :”#{name}=”, nil
assert !u.valid?
assert u.errors.invalid?(:”#{name}”)
end
“)
end
end
I jej użyjmy:
test_validates_presence_of :name, :middlename, :surname, :sex, :birthday
It’s more rails style
A ja się doczepię o ortografię.
Powinno być “załóżmy”, a nie “zaużmy”.
Wstyd wstyd wstyd, ale błędu już nie ma
@Marek
Faktycznie, twój sposób jest elegancki, ale pozbądźmy się class_eval. Można wtedy zapisać to w ten sposób:
def self.test_validates_presence_of(*args)
args.each do |name|
define_method(:”test_validation_#{name}”) do
u = users(‘user1′)
u.send :”#{name}=”, ”
assert !u.valid?
assert u.errors.invalid?(:”#{name}”)
u.send :”#{name}=”, nil
assert !u.valid?
assert u.errors.invalid?(:”#{name}”)
end
end
end
Żeby się pozbyć redundancji to można blok send, assert, assert wyekstraktować do osobnej metody i jest ‘zahardcodowane’ używanie z fixtures user1, które to też należałoby zmienić.
Wszystko fajnie tylko testowanie tego to jest troche overkill. Wlasne metody to rozumiem, popieram i stosuje. Ale takie rzeczy jak validates_* to juz mi pachnie nadgorliwoscia podobna do testowania np. attr_reader.