Useful helpers for RSpec mocks

May 05
2007 02:38 (Development, RSpec, Ruby on Rails) · Русский (11,366 views)

RSpec has great feature — mock objects (mocks). In a few words: mock object imitates behavior of the object, which used by the tested methods. And simple example immediately:

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

Well, and what does it mean? Initially we are creating mock object @user, which would be used instead of real model. Please don’t ask me why do we need such complexity and why we can’t just use real model. I will explain it myself. On the one hand, mocks are much faster than the database operations — when you have many tests (and you have, right?) total tests execution time would be much smaller if we would use mock objects. On the other hand, you don’t want to test the same methods again and again, right? In my example preferred_name method has non-trivia logic (it’s not simple database field), and it has already been tested in model spec. Imagine that you are using model in helper tests. If this method would break down, two specifications will be failed instead of one — model specification. In addition, there is one interesting feature exists — rcov, which shows how your code is covered by tests. Thus if model tests would not exist, and helper tests would use model,– rcov will show preferred_name method as covered, but it is not true. Oops, I have been distracted.

Oh yes, if you don’t know, stub is just method, which does nothing, it just return value, which you have passed to it.

So we have a test. Could we simplify it somehow? Yep!

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

Much better. Helper method add_stubs adds to the object, passed as a first parameter, stubs, passed as a second parameter in hash. But it’s not all. Especially for the Active Record models RSpec has method mock_model, which automatically creates several stubs, common for all Ruby on Rails models, and accepts hash with stubs just like add_stubs does:

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

You definitely noticed that I have missed parameters :id and :new_record?, and it was not coincidence. Firstly, mock_model automatically defines unique ids for models, which were created using it. Secondly, it defines methods to_param (returns string representation of the id) and new_record? (returns false). The first parameter of the method is a model class, and the second one is a hash of stubs, as I have already said. That’s all, folks.

4 Responses to 'Useful helpers for RSpec mocks'

Subscribe to comments with RSS or TrackBack to 'Useful helpers for RSpec mocks'.

1
liquidautumn
said on 2007-05-06 at 3.17 am

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

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
said on 2007-05-06 at 2.26 pm

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

3
said on 2008-05-06 at 3.34 pm

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

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

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

4
said on 2008-05-06 at 3.49 pm

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

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

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

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

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

Post a comment

You can use simple HTML-formatting tags (like <a>, <ul> and others). To format your code sample use <code lang="php">$a = "hello";</code> (allowed languages are ruby, php, yaml, html, csharp, javascript). Also you could use <code>$a = "hello";</code> and its syntax would not be highlighted. If you are not using <code> tag, replace < sign with &lt;.

Submit Comment

 
Copyright © 2005 - 2008, Dmytro Shteflyuk