Удобные хелперы для мок-объектов RSpec

May 05
2007 02:38 (Программирование, RSpec, Ruby on Rails) · English (7,485 views)

В RSpec есть такая замечательная штука, как мок-объекты (mocks). В двух словах: мок-объект имитирует поведение объекта, который используется тестируемыми методами. Сразу простой пример:

describe UserHelper
  it 'should generate correct link to user profile in user_link' do
    @user = mock('User')
    @user.stub!(:id, 10)
    @user.stub!(:new_record?, false)
    @user.stub!(:preferred_name, 'Dmytro S.')
    @user.stub!(:full_name, 'Dmytro Shteflyuk')
    user_link(@user).should == link_to('Dmytro S.', user_url(:id => 10), :title => 'Dmytro Shteflyuk')
  end
end

Итак, что же тут написано. Сначала мы создаем мок-объект @user, который будет выступать в роли реальной модели. Только не надо меня спрашивать, зачем такие сложности и почему нельзя просто использовать реальную модель. Я сам расскажу. Во-первых, моки намного быстрее, чем работа с базой — при большом количестве тестов (а у вас ведь их много, правда?) общее время выполнения тестов будет в несколько раз меньше при работе с моками. Во-вторых, вы ведь не хотите тестировать одни и те же методы по сто раз? В моем примере метод preferred_name имеет нетривиальную логику (это не просто поле из базы), и он уже протестирован в спецификации модели. При использовании модели в тестах хелпера, если работа этого метода нарушается, свалятся тесты сразу в двух местах, что не очень удобно. Кроме того, есть такая интересная вещь rcov, которая показывает, какая часть кода покрыта тестами. Так вот если бы тестов модели не было, а в тестах хелпера использовалась модель,– rcov показал бы метод preferred_name покрытым, что не совсем верно. Что-то я отвлекся.

Да, если кто не в курсе, stub (заглушка) — это метод, который ничего не делает, а просто возвращает значение, какое вы ему укажете.

Итак, у нас есть тест. Может его можно как-то упростить? Да!

describe UserHelper
  it 'should generate correct link to user profile in user_link' do
    @user = mock('User')
    add_stubs(@user, :id => 10, :new_record? => false, :preferred_name => 'Dmytro S.', :full_name => 'Dmytro Shteflyuk')
    user_link(@user).should == link_to('Dmytro S.', user_url(:id => 10), :title => 'Dmytro Shteflyuk')
  end
end

Уже немного приятнее. Вспомогательный метод add_stubs добавляет к объекту, указанному первым параметром, заглушки, которые передаются во втором параметре в виде хэша. Но это еще не все. Специально для моделей Active Record в RSpec есть метод mock_model, который автоматически создает несколько заглушек, стандартных для моделей Ruby on Rails, и принимает хэш с заглушками, как add_stubs:

describe UserHelper
  it 'should generate correct link to user profile in user_link' do
    @user = mock_model(User, :preferred_name => 'Dmytro S.', :full_name => 'Dmytro Shteflyuk')
    user_link(@user).should == link_to('Dmytro S.', user_url(:id => @user.id), :title => 'Dmytro Shteflyuk')
  end
end

Вы наверняка обратили внимание, что я опустил параметры :id и :new_record?, и сделал это не случайно. Во-первых, этот метод автоматически раздает уникальные idы моделям, которые создаются с его помощью, во-вторых — определяет методы to_param (возвращает строковое представление id) и new_record? (возвращает false). Первым параметром метод принимает класс модели, вторым — хэш заглушек, но это я уже говорил. Вот в принципе и все.

4 отзывов на 'Удобные хелперы для мок-объектов RSpec'

Подписаться на комментарии по RSS или TrackBack на 'Удобные хелперы для мок-объектов RSpec'.

1
liquidautumn
сказал 06.05.2007 в 3.17

поправь пример:

describe UserHelper
  it 'should generate correct link to user profile in user_link' do
    @user = mock_model(User, :preferred_name => 'Dmytro S.', :full_name => 'Dmytro Shteflyuk')
    user_link(@user).should == link_to('Dmytro S.', user_url(:id => @user.id), :title => 'Dmytro Shteflyuk')
  end
end
2
сказал 06.05.2007 в 14.26

Спасибо, провтыкал :-)

3
сказал 06.05.2008 в 15.34

А как быть с проверкой модели на валидность? Я попробовал такой код:

it "should be invalid withoud user" do
    @comment = mock_model(Comment, :user => nil)
    @comment.should_not be_valid
  end

и получил ответ, что сообщение :valid? неизвестно объекту @comment.

4
сказал 06.05.2008 в 15.49

Нужно еще добавить строчку

@comment.stub!(:is_valid?, false)

Либо передать ее в mock_model

@comment = mock_model(Comment, :user => nil, :is_valid? => false)

Когда используется mock_model, создается не полноценная модель, а только ее “тень”. Потому специфические методы нужно стабить руками.

Оставить отзыв

Вы можете использовать простые теги форматирования HTML (вроде <a>, <ul> and others). Чтобы вставить пример код, используйте <code lang="php">$a = "hello";</code> (поддерживаемые языки: ruby, php, yaml, html, csharp, javascript). Также Вы можете использовать <code>$a = "hello";</code>, синтаксис не будет подсвечен. Если вы не хотите использовать тег <code>, замените символ < на &lt;.

Отправить

 
Copyright © 2005 - 2008, Dmytro Shteflyuk