Sprytne testowanie modelu w Ruby on Rails

napisane przez wiktor, 20:09 10-10-2007

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:
  1. class User < ActiveRecord::Base
  2.  
  3.     validates_presence_of :name, :middlename, :surname, :sex, :birthday  
  4.  
  5. end

Zakładam, że jestem uczciwy względem siebie i chce przetestować, czy walidacje działają poprawnie. Zaczynam więc pisać test:

RUBY:
  1. class UserTest <Test::Unit::TestCase
  2.   fixtures :users
  3.  
  4.   def test_validation_name
  5.       u = users('user1')
  6.  
  7.       u.name = ''
  8.       assert !u.valid?
  9.       assert u.errors.invalid?("name")
  10.  
  11.       u.name = nil
  12.       assert !u.valid?
  13.       assert u.errors.invalid?("name")
  14.  
  15.   end
  16.  
  17.   # ...i tak dalej... nudny w sumie kod....
  18. end

Ale przecież możemy skorzystać z dynamicznej natury Ruby'iego i wstrzyknąć wygenerowane wcześniej metody. Poniżej widać, jak to zrobić:

RUBY:
  1. class UserTest <Test::Unit::TestCase
  2.   fixtures :users
  3.  
  4.   #tworzę tablicę z poniższych elementów i iteruję po niej
  5.   %w(name middlename surname sex birthday).each do  |name|
  6.    
  7.     #definiuję nową metodę do testowania poprawności walidacji
  8.     define_method(:"test_validation_#{name}") do
  9.  
  10.         u = users('user1')
  11.  
  12.         #dynamiczne wywołanie metody, a dokładnie przypisanie pustego napisu do pola
  13.         u.send :"#{name}=", ''
  14.         assert !u.valid?
  15.         assert u.errors.invalid?(:"#{name}")
  16.  
  17.         u.send :"#{name}=", nil
  18.         assert !u.valid?
  19.         assert u.errors.invalid?(:"#{name}")
  20.     end
  21.   end
  22.  
  23. end

Piękne rozwiązanie, prawda? :)

Komentarzy (6)

  1. Marek napisał(a):

    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

  2. Marek Kirejczyk napisał(a):

    It's more rails style ;)

  3. Daj Pan Spokój napisał(a):

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

  4. wiktor napisał(a):

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

  5. wiktor napisał(a):

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

  6. Tomek napisał(a):

    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.

Zostaw komentarz

Możesz używać znaczników do formatowania kodu takich jak: <b>...</b>, <code>...</code> lub dla konkretnych języków programowania: [java]...[/java], [ruby]...[/ruby] itd.


Wiktor Gworek Nazywam się Wiktor Gworek i jestem gospodarzem tego bloga.
Przeczytaj więcej o mnie »