---
title: Testing ActiveRecord Named Scopes
teaser:
tags: web,testing,ruby,rails
author: Dan Croak
published_on: 2009-09-29
---

Everyone was psyched when Nick Kallen's [`has_finder` plugin was added to Rails
as
`named_scope`](http://ryandaigle.com/articles/2008/3/24/what-s-new-in-edge-rails-has-finder-functionality).
They're powerful, particularly when chaining.

One disadvantage is that they are so easy to create, people want their tests to
be equally concise, which is often impossible.

Two related "testing named scopes" questions have come up recently on
mailing lists.

## Movie recommendations: InternetFlicks

We're going to create a movie recommendation system called InternetFlicks.

<kbd>
script/generate model Movie ranking:integer in\_stock:boolean
<br />
script/generate model Viewing user:belongs\_to movie:belongs_to
</kbd>

We'll assume a `User` model is already created,
perhaps using [Clearance](https://github.com/thoughtbot/clearance).

## Outside-In

Say we're working on the recommendations page for the signed in user. With
RESTful routing this might be `/recommendations`.

We can use a [simple authorization
strategy](https://thoughtbot.com/blog/post/161233418/analysis-paralysis-access-control):

```ruby
class RecommendationsController < ApplicationController
  before_filter :authenticate

  def index
    @movies = Movie.recommended_for(current_user)
  end
end
```

I'm skipping a few steps here, but our goal is to determine what interface the
model needs to expose.

Also note that we're intentionally crossing resources ("recommendations") in
the RESTful sense with a model of a different name ("Movie"). This is something
we've started to stress in training
as many students have thought REST means they need to match controller names to
models (probably because they've seen scaffold generation).

## TDD will guide you to better habits

Use [Role Suggesting
Name](https://thoughtbot.com/blog/post/191132156/role-suggesting-name) for the
test name, variable names, and method under test to describe the behavior of
`Movie.recommended_for`.

```ruby
class MovieTest < ActiveSupport::TestCase
  should "recommend 2 highest ranked, in stock movies unwatched by user" do
    user    = Factory(:user)
    top_out = Factory(:movie, :ranking => 100, :in_stock => false)
    top_in  = Factory(:movie, :ranking => 95,  :in_stock => true)
    next_in = Factory(:movie, :ranking => 90,  :in_stock => true)
    watched = Factory(:movie, :ranking => 100, :in_stock => true)
    Factory(:viewing, :user => user, :movie => watched)

    assert_equal [top_in, next_in], Movie.recommended_for(user, 2)
  end
end
```

This is a state-based test. We create a user and a few movies, have him or her
watch a movie, exercise the method, and verify the results match the test name.

Skipping ahead again, say we've gone through a few red, green, refactor cycles,
taking into account some edge cases, and the implementation now looks like
this:

```ruby
class Movie < ActiveRecord::Base
  def self.recommended_for(user, limit = 10)
    highest_ranked.in_stock.unwatched(user).limited(limit)
  end

  private

  named_scope :highest_ranked, :order => "ranking desc"
  named_scope :in_stock, :conditions => { :in_stock => true }
  named_scope :unwatched, lambda { |user|
    { :joins      => "left outer join viewings
                                    on viewings.movie_id = movies.id
                                  and viewings.user_id  = #{user.id}
                      left outer join users
                                    on users.id = viewings.id",
      :conditions => "users.id is null" }
  }
end
```

Cool, `named_scopes` helped us make the `recommended_for` method expressive. If
the specification for it changes, it will be a joy to change.

## No tests for the private methods

We don't need to test private methods.

If an object outside of `Movie` needs `highest_ranked` or one of the other
private methods, we'll write a unit test for it, watch it error, and bring it
into a public scope piece by piece.

## Discarded option: `should_have_named_scope`

About a year ago, [`should_have_named_scope` was introduced to
Shoulda](https://thoughtbot.com/blog/post/159806857/testing-named-scope).

It was later deprecated. We never felt comfortable using it. While there's
something to be said for quick Shoulda one-liners, I don't think there's much
of an argument for `should_have_named_scope` except for simple scopes that are
responsible for order or limit.

Since those are usually taken care of by something like
[`utility_scopes`](http://github.com/yfactorial/utility_scopes) or
[Pacecar](http://github.com/thoughtbot/pacecar) (and thus do not need to be
unit tested) OR are often relegated to private status, that doesn't leave a
niche for `should_have_named_scope`.

If you're still a `should_have_named_scope` fan, please write a test for
`unwatched` using it. I'll be surprised if it stands up to peer review.

## Discarded option: `stub_chain`

Another option I've been using this summer, but have recently rejected is
`stub_chain`.

```ruby
module StubChainMocha
  module Object
    def stub_chain(*methods)
      while methods.length > 1 do
        stubs(methods.shift).returns(self)
      end
      stubs(methods.shift)
    end
  end
end

Object.send(:include, StubChainMocha::Object)
```

This approach unit tests each of the named scopes and then `stub_chains` the
class method:

```ruby
should "find 10 highest ranked movies in stock that you have not seen" do
  user = Factory(:user)
  Movie.stub_chain(:highest_ranked, :in_stock, :unwatched, :limited)

  Movie.recommended_for(user)

  assert_received(Movie, :highest_ranked)
  assert_received(Movie, :in_stock)
  assert_received(Movie, :unwatched) { |expect| expect.with(user) }
  assert_received(Movie, :limited)   { |expect| expect.with(10) }
end
```

This is understandable, but very tied to implementation, does not test the
"integration" of the scopes (dangerous in many situations when the resulting
SQL is not combined as expected), and stubs out methods on the same object
(usually a smell that something needs to be refactored).

My suggested approach may often be more lines of code, but will better describe
the behavior.

## No chains in controllers

Some of us have started to use a rule of thumb of "no chains in controllers".
This makes the testing decisions easier, and makes the model's public
interfaces and public/private interface distinction cleaner. The end result is
usually a class method that wraps (potentially private) named scopes.
