Testing named_scope

Posted by Tammer Saleh

Jul 29

We’re huge fans of NamedScope here at Thoughtbot. It does wonders for finder reuse and clarity. Unfortunately, it also creates a large number of finders that all must be tested, where the old method may have only created one.

To help out with the testing of simple named_scope definitions, we added a helper to Shoulda, should_have_named_scope:

1
2
3
4
5
6
7
8
9
10
11
12
13

class User < ActiveRecord::Base
  named_scope :old,      :conditions => "age > 50"
  named_scope :eighteen, :conditions => { :age => 18 }
  named_scope :recent,   lambda {|count| { :limit => count } }
end

class UserTest < Test::Unit::TestCase
  should_have_named_scope :old,       :conditions => "age > 50"
  should_have_named_scope :eighteen,  :conditions => { :age => 18 }
  should_have_named_scope 'recent(5)', :limit => 5
  should_have_named_scope 'recent(1)', :limit => 1
end

Now for complex methods, where the options returned by the has_finder are hairier than a simple limit or a single column condition, we would also write black box tests. But this helper is great for those simpler calls.


Comments on this post

theJareCare

Jul 29

theJareCare said,

i’d use this helper if it were actually testing my code ;). all this is doing is duplicating the implementation, basically testing that i typed in the correct characters. there’s no testing of the actual functionality.

i prefer traditional state-based testing i.e. setting up local fixtures, exercising the code and then running assertions on the results. yes, its more code but its a full test; these helpers are like partial tests only testing the fact that im using #named_scope (im assuming they’re using Scope#proxy_options which would fail if i wasn’t)

- jc

Carlos Brando

Jul 30

Carlos Brando said,

Bundle updated! http://github.com/carlosbrando/ruby-shoulda-tmbundle/tree/master

John

Jul 30

John said,

Hi, I’ve been trying to get my head around Rails testing, and I have to agree with the first poster (theJaleCare). What is the example actually testing? It looks like it’s just testing the framework code. And if that hasn’t been tested, there’s a much bigger problem! Most tests I’ve seen, and testing tutorials pretty much do the same thing – test_added_to_database. It’ll add a record to the DB and then check to see if it’s there. Again, if that functionality didn’t work, there’s a huge problem!

Can someone point me to some examples of testing that actually test something besides the framework? I’ve looked at many various Rails apps and have yet to find tests that are beyond the simple “add an item to the db, is there now one item in the table?” type.

Thanks!

Tammer Saleh

Jul 30

Tammer Saleh said,

John & theJareCare: Here’s an example of where you would be testing the finder logic, and not the SQL that it produces.

1
2
3
4
5
6
7
8
9
10
11

class User
  def self.sorted(key)
    case key
    when "name":  scoped(:order => "first_name DESC")
    when "score": scoped(:order => "total_score DESC")
    else
      scoped(:order => "popularity")
    end
  end
end

Using blackbox testing for this method would definitely result in greater test coverage. It would also mean that you’d have to create a large number of records, each with different names, scores, and popularity rankings, and assert that they’re returned in the right order. The tests would also have to ensure that the object returned can be chained with other finders.

Compare that to:

1
2
3
4
5
6

class UserTest
  should_have_finder 'sorted("name")',  :order => "first_name DESC"
  should_have_finder 'sorted("score")', :order => "score DESC"
  should_have_finder 'sorted',          :order => "popularity"
end

And to be clear: the only thing we aren’t testing here is that an SQL call with order set to “first_name DESC” returns the records ordered by first_name. That is a definite gap in the test coverage. In return for that gap, you get test suite maintainability.

theJareCare

Jul 30

theJareCare said,

you admit there’s a gap in the test coverage and in return that gives you ‘test suite maintainability’? so you prefer less code and shotty tests over a few more lines of test code and tests that actually test the functionality of your app?

lines of code is a poor metric for determining that an app is not maintainable, only the quality of those tests should be used in determining maintainability. lines of code can only give you a rough indicator of whether an app is tested at all, in no way can it tell you what is and what isn’t tested or how effective those tests are.

Eric Anderson

Jul 30

Eric Anderson said,

I agree with the first poster. This is not meant to criticize the test helper you have developed but just something I have been thinking about in general. I feel like a lot of tests are:

1. Add a Record 2. Make sure that record exists

Obviously if you are developing an ORM then you need test like the above. But on the other hand if you are just using a ORM then you should rely on that library’s testing (and if it is insufficient then test there). Another example. If I have the following:

1
2
3
4
5
6

class Client < ActiveRecord::Base
  has_many :employees
  has_many :projects
  belongs_to :division
end

In my mind there is nothing here that needs to be tested. We are purely just using the library and all testing would do is make sure we typed the characters right (and if we doubt that what makes us think we can type the test characters right).

The only time you need to test is in YOUR projects code. For example if I add a complex :conditions parameter I might test it (but simple :conditions I probably won’t because again we are just using the library). Or if I add a before_filter I might test that. In general my rules are:

  • Am I using the library in a simple and expected way?
  • Is my code purely declarative and I am relying on a 3rd party for implementation of that declarative statement?

If the answer to either of these questions is YES then I often don’t see a need for testing. You are just increasing the size of your code base without really doing anything.

Morgan Roderick

Aug 01

Morgan Roderick said,

We have been using shoulda since it’s early public releases, and welcome these updates, as they will be valuable in sketching out the scopes that other functionality in the system will rely on.

The complaints about lacking test coverage is a bit strange to me. Shoulda now provides an easier way to quickly sketch out requirements for a named scope, which is part of the public interface of a model. If you want to ensure quality test coverage, you should use your brain and not rely entirely on tests written using a dsl … but a few tests written using a dsl will get you off the ground quickly.

How much complexity would it add to Shoulda, to have exhaustive end-to-end tests for named scopes? How often would you use these? Would the value of such tests outweigh the costs?

@Eric: Is testing the public interface of a class of less value to YOU than testing if a record has been written to a database, or some condition is set to a specific value?

I find that in colloboration with other developers, having tests for the public interfaces is of great value, as you might think twice about modifying the public interface when you start breaking tests :-)


Sorry, comments are closed for this article.

© 2000 - 2009 by thoughtbot, inc.
written by a bushel of tiny robots