Krzysztof Zalewski

zlw@github:pages

How to Proper Test Named Scopes

I just read few posts about “how to test named scopes”. It was tons of bullshit. I am asking – why should I use FactoryGirl or raw models and create a lot of unnecessary stuff (objects, db rows etc.) just to test order(:position).first?!

But first, let’s create Post model. Just an example for this note.

post.rb
1
2
3
4
5
6
7
8
9
10
11
class Post < ActiveRecord::Base
  belongs_to :user

  def self.recent
    order('created_at DESC').first
  end

  def self.for_user(user_id)
    where(user_id: user_id)
  end
end

As you see, there are 2 simple scopes. I am not huge fan of Rails’s named scopes – I use plain old class methods instead.

How not to do this

post_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
require 'spec_helper'

describe Post do
  context '#recent' do
    it 'should return most recent post' do
      _, post = [Factory(:post), Factory(:post)]

      Post.recent.should == post
    end
  end

  context '#for_user' do
    it 'should return only posts created by given user' do
      post1, post2, _ = [Factory(:post, user_id: 1), Factory(:post, user_id: 1), Factory(:post, user_id: 2)]

      Post.for_user(1).should == [post1, post2]
    end
  end
end

We’ve just created .. let’s count .. 5 objects and made 2 unnecessary db queries.

How to do this

post_spec.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
require 'spec_helper'

describe Post do
  context '#recent' do
    it 'should return most recent post' do
      post = mock_model Post

      Post.should_receive(:order).with('created_at DESC').and_return post
      post.should_receive(:first).and_return post

      Post.recent
    end
  end

  context '#for_user' do
    it 'should return only posts created by given user' do
      post = mock_model Post

      Post.should_receive(:where).with(user_id: 1)
      Post.for_user 1
    end
  end
end

I think it’s useless to test Rails’s elements (like where or order). Rails is really proper tested. Instead we should spec messages between object and passed parameters.

Comments