unittest | Dmytro Shteflyuk's Home https://kpumuk.info In my blog I'll try to describe about interesting technologies, my discovery in IT and some useful things about programming. Tue, 08 Sep 2015 00:20:18 +0000 en-US hourly 1 https://wordpress.org/?v=6.7.1 Useful helpers for RSpec mocks https://kpumuk.info/ruby-on-rails/useful-helpers-for-rspec-mocks/ https://kpumuk.info/ruby-on-rails/useful-helpers-for-rspec-mocks/#comments Sat, 05 May 2007 00:38:39 +0000 http://kpumuk.info/rspec/useful-helpers-for-rspec-mocks/ 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: 12345678910describe UserHelper   it 'should generate correct link to user profile in user_link' do     @user = mock('User')     @user.stub!(:id, 10)     @user.stub!(:new_record?, […]

The post Useful helpers for RSpec mocks first appeared on Dmytro Shteflyuk's Home.]]>
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:

1
2
3
4
5
6
7
8
9
10
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!

1
2
3
4
5
6
7
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:

1
2
3
4
5
6
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.

The post Useful helpers for RSpec mocks first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/ruby-on-rails/useful-helpers-for-rspec-mocks/feed/ 5
Testing mailers with RSpec https://kpumuk.info/ruby-on-rails/testing-mailers-with-rspec/ https://kpumuk.info/ruby-on-rails/testing-mailers-with-rspec/#comments Thu, 19 Apr 2007 05:42:22 +0000 http://kpumuk.info/rspec/testing-mailers-with-rspec/ Unfortunately, RSpec does not provide helpers for testing mailers like TestUnit. But it is easy to add them to your application, and here you could find code snippet for testing mailers. For the beginning we would define helper for reading fixtures and put them into the spec/mailer_spec_helper.rb file: 123456789require File.dirname(__FILE__) + '/spec_helper.rb' module MailerSpecHelper   […]

The post Testing mailers with RSpec first appeared on Dmytro Shteflyuk's Home.]]>
Unfortunately, RSpec does not provide helpers for testing mailers like TestUnit. But it is easy to add them to your application, and here you could find code snippet for testing mailers.

For the beginning we would define helper for reading fixtures and put them into the spec/mailer_spec_helper.rb file:

1
2
3
4
5
6
7
8
9
require File.dirname(__FILE__) + '/spec_helper.rb'

module MailerSpecHelper
  private

    def read_fixture(action)
      IO.readlines("#{FIXTURES_PATH}/mailers/user_mailer/#{action}")
    end
end

Now we need to create fixtures for mailers in the spec/fixtures/mailers folder, each mailer in the separate subfolder like spec/fixtures/mailers/some_mailer/activation:

1
Hello, Bob

In spec/models/mailers/some_mailer_spec.rb we would write something like:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
require File.dirname(__FILE__) + '/../../mailer_spec_helper.rb'

context 'The SomeMailer mailer' do
  FIXTURES_PATH = File.dirname(__FILE__) + '/../../fixtures'
  CHARSET = 'utf-8'

  fixtures :users

  include MailerSpecHelper
  include ActionMailer::Quoting

  setup do
    # You don't need these lines while you are using create_ instead of deliver_
    #ActionMailer::Base.delivery_method = :test
    #ActionMailer::Base.perform_deliveries = true
    #ActionMailer::Base.deliveries = []

    @expected = TMail::Mail.new
    @expected.set_content_type 'text', 'plain', { 'charset' => CHARSET }
    @expected.mime_version = '1.0'
  end

  specify 'should send activation email' do
    @expected.subject = 'Account activation'
    @expected.body    = read_fixture('activation')
    @expected.from    = '[email protected]'
    @expected.to      = users(:bob).email

    Mailers::UserMailer.create_activation(users(:bob)).encoded.should == @expected.encoded
  end
end

That’s almost all. We are fully confident that emails look as we expected. In controller we don’t need to re-test mailers again, we just need to become convinced of mailer calling!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
context 'Given an signup action of UserController' do
  controller_name 'user'

  setup do
    @user = mock('user')
    @valid_user_params = { :email => '[email protected]' }
  end

  specify 'should deliver activation email to newly created user' do
    User.should_receive(:new).with(@valid_user_params).and_return(@user)
    Mailers::UserMailer.should_receive(:deliver_activation).with(@user)
    post :signup, :user => @valid_user_params
    response.should redirect_to(user_activate_url)
  end
end
The post Testing mailers with RSpec first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/ruby-on-rails/testing-mailers-with-rspec/feed/ 3
5 Things why I love RSpec https://kpumuk.info/ruby-on-rails/5-things-why-i-love-rspec/ https://kpumuk.info/ruby-on-rails/5-things-why-i-love-rspec/#comments Mon, 19 Mar 2007 07:31:02 +0000 http://kpumuk.info/ruby-on-rails/5-things-why-i-love-rspec/ RSpec provides a framework for writing what can be called executable specifications of program behavior. In this short post I want to explain why I use this framework in place of classic TestUnit library. I stopped getting confused with order of expected and real values. Compare: 1assert('login', @user.login) and [email protected] == 'login' BTW, I made […]

The post 5 Things why I love RSpec first appeared on Dmytro Shteflyuk's Home.]]>
RSpec provides a framework for writing what can be called executable specifications of program behavior. In this short post I want to explain why I use this framework in place of classic TestUnit library.

  1. I stopped getting confused with order of expected and real values. Compare:

    1
    assert('login', @user.login)

    and

    1
    @user.login.should == 'login'

    BTW, I made a mistake in the assert_equal parameters order when was writing this article.

  2. I could understand what objects should do at first look on the test:

    1
    2
    3
    4
    5
    6
    7
    context 'The User model with fixtures loaded' do
      specify 'should be friended by the user if this user added him to friends' do
        users(:bob).friends << users(:chris)
        users(:chris).should have(1).friended
        users(:chris).friended.first.should == users(:bob)
      end
    end
  3. Test results are presented in easy to understand and clear form, just simple sentences, which are making asserts about entities in the system. In most cases there is even no necessity to look at the tests to understand which functionality has one or another entity!

    1
    2
    3
    4
    5
    6
    7
    8
    9
    The newly created User
    - should be valid with all neccessary attributes specified
    - should be invalid without login
    - should be invalid when login length is less than 2
    - should be invalid without email
    - should be invalid with wrong email
    - should be invalid with someone else's login
    - should be invalid with someone else's email
    - should not require password and password_confirmation
  4. I started writing tests before actual coding finally! In fact, every developer think over what one or another method should do, and frequently these thoughts looks like “when method received string ‘somestring’ it should return ‘anotherstring'”. It’s easy to describe these thoughts using BDD-tests.

  5. Integrated flexible mock-objects system allows testing functionality just once. For example, when you test controllers, you should not use database and models – it is sufficient to check, if necessary model methods would be called in right order with right parameters.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    context 'Given an signup action of UserController' do
      controller_name 'user'
     
      setup do
        @user = mock('user')
        add_stubs(@user, :password= => nil, :password_confirmation= => nil, :new_password= => nil)
      end
     
      specify 'should render edit template with empty User object' do
        User.should_receive(:new).with(:no_args).and_return(@user)
        get :signup
        response.should render_template(:edit)
        assigns[:user].should be(@user)
      end
    end

The post 5 Things why I love RSpec first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/ruby-on-rails/5-things-why-i-love-rspec/feed/ 3
Create your fixtures easily https://kpumuk.info/ruby-on-rails/create-your-fixtures-easily/ https://kpumuk.info/ruby-on-rails/create-your-fixtures-easily/#comments Sat, 20 Jan 2007 21:09:46 +0000 http://kpumuk.info/ruby-on-rails/create-your-fixtures-easily/ Every time I’m creating fixtures for Rails application I’m being angry because any more or less considerable project needs tons of them. Most complicated thing is to track all relationships, validations, and to keep fixtures up to date. A few days ago Yurii Rashkovskii has released small utility — Fixturease. What if I would tell […]

The post Create your fixtures easily first appeared on Dmytro Shteflyuk's Home.]]>
Every time I’m creating fixtures for Rails application I’m being angry because any more or less considerable project needs tons of them. Most complicated thing is to track all relationships, validations, and to keep fixtures up to date. A few days ago Yurii Rashkovskii has released small utility — Fixturease. What if I would tell you that now you can create your fixtures using your models?

To install gem enter command:

1
gem install fixturease

It’s time to try something:

1
2
3
4
5
6
7
8
9
10
11
12
# Loading Fixturease console for test mode.
@mike = User.create(:login => 'mike', :password => 'pass')
# <User:0x482ac18 @new_record_before_save=true, @attributes={"id"=>5, "password"=>"pass", "login"=>"mike", "created_at"=>Sat Jan 20 21:40:23 +0200 2007}, @errors=#<ActiveRecord::Errors:0x4827b08 @base=#<User:0x482ac18 ...>, @errors={}>, @new_record=false>
@mike_post_1 = @mike.posts.create(:name => 'first post by mike')
# <Post:0x4811bb4 @new_record_before_save=true, @attributes={"name"=>"first post by mike", "updated_at"=>Sat Jan 20 21:40:38 +0200 2007, "id"=>3, "user_id"=>5, "created_at"=>Sat Jan 20 21:40:38 +0200 2007}, @errors=#<ActiveRecord::Errors:0x4811510 @base=#<Post:0x4811bb4 ...>, @errors={}>, @new_record=false>
@mike_post_2 = @mike.posts.create(:name => 'second post by mike')
# <Post:0x48099dc @new_record_before_save=true, @attributes={"name"=>"second post by mike", "updated_at"=>Sat Jan 20 21:40:45 +0200 2007, "id"=>4, "user_id"=>5, "created_at"=>Sat Jan 20 21:40:45 +0200 2007}, @errors=#<ActiveRecord::Errors:0x48093d8 @base=#<Post:0x48099dc ...>, @errors={}>, @new_record=false>
exit

# Saving fixtures:
# User [./spec/fixtures/users.yml]: mike
# Post [./spec/fixtures/posts.yml]: mike_post_1 and mike_post_2

In this very simple case we just created one fixture for User model named mike and two for Post model named mike_post_1 and mike_post_2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
mike:
  id
: 5
  password
: pass
  login
: mike
  created_at
: 2007-01-20 21:40:23.250000 +02:00

mike_post_1
:
  name
: first post by mike
  updated_at
: 2007-01-20 21:40:38.765000 +02:00
  id
: 3
  user_id
: 5
  created_at
: 2007-01-20 21:40:38.765000 +02:00
mike_post_2
:
  name
: second post by mike
  updated_at
: 2007-01-20 21:40:45.562000 +02:00
  id
: 4
  user_id
: 5
  created_at
: 2007-01-20 21:40:45.562000 +02:00

You can always load fixtures to current environment using command load_fixtures! (or command-line switch -l). It allows you to use previously created fixtures to build new ones.

I like unit tests but I hate to write fixtures. This nice gem allows me to relax and to get pleasure from work.

The post Create your fixtures easily first appeared on Dmytro Shteflyuk's Home.]]>
https://kpumuk.info/ruby-on-rails/create-your-fixtures-easily/feed/ 3