Sprytne testowanie modelu w Ruby on Rails

Posted by wiktor on Oct 10, 2007 in 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? :)

Related Posts with Thumbnails

6 Comments

Marek
Oct 11, 2007 at 12:22 am

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


 
Marek Kirejczyk
Oct 11, 2007 at 12:30 am

It’s more rails style ;)


 
Daj Pan Spokój
Oct 11, 2007 at 8:31 am

A ja się doczepię o ortografię.
Powinno być “załóżmy”, a nie “zaużmy”. ;-)


 
wiktor
Oct 11, 2007 at 11:05 am

Wstyd wstyd wstyd, ale błędu już nie ma :)


 
wiktor
Oct 11, 2007 at 11:21 am

@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ć.


 
Tomek
Oct 13, 2007 at 2:02 pm

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.


 

Reply

Copyright © 2012 Mocna Kawa All rights reserved. Theme by Laptop Geek.