Rails: Testing named scopes with RSpec
Solution 1
The creator of RSpec has recently blogged he thinks Validations are behavior, associations are structure. In other words he finds that associations (and scopes) should not nessesarily be tested directly. Tests for these will follow from the behavior you want.
In other words, current wisdom is that there is no need to test each scope directly, since you will cover these associations by testing the behavior of your application.
Solution 2
David Chelimsky testing scopes (updated)
David Chelimsky example, (linked by Sam Peacey's comment), modernised.
# app/models/user.rb
class User < ActiveRecord::Base
scope :admins, -> { where(admin: true) }
end
# spec/models/user_spec.rb
RSpec.describe User, type: :model do
describe ".admins" do
it "includes users with admin flag" do
admin = User.create!(admin: true)
expect(User.admins).to include(admin)
end
it "excludes users without admin flag" do
non_admin = User.create(admin: false)
expect(User.admins).not_to include(non_admin)
end
end
end
This produces a more 'spec-like' output (when using --format documentation):
User
.admins
includes users with admin flag
excludes users without admin flag
Note about origination of this answer:
David Chelimsky, the RSpec lead at the time, answered this question and Sam Peacey's link to it has more votes than the actual answer. The answer is not easy to find and follow as he is replying to someone and editing their answer in an email chain. This answer cleans that up and updates the RSpec code as, I guess, he would have written it today.
Solution 3
From https://coderwall.com/p/hc8ofa/testing-rails-model-default_scope-with-rspec
- no database queries
- no need to represent the query in a structure
Example:
class Trip < ActiveRecord::Base
default_scope { order(departure: :asc) }
...
end
RSpec.describe Trip, type: :model do
it "applies a default scope to collections by departure ascending" do
expect(Trip.all.to_sql).to eq Trip.all.order(departure: :asc).to_sql
end
end
andrykonchin
Updated on May 10, 2021Comments
-
andrykonchin almost 3 years
I am new to testing Rails web applications and RSpec. I work with legacy code and need to add tests. So what is the best way to test finders and named scopes with RSpec?
I find in Google a few approaches but they are not ideal. For example:
http://paulsturgess.co.uk/articles/show/93-using-rspec-to-test-a-named_scope-in-ruby-on-rails
it "excludes users that are not active" do @user = Factory(:user, :active => false) User.active.should_not include(@user) end
or
http://h1labs.com/notebook/2008/8/21/testing-named-scope-with-rspec
it "should have a published named scope that returns ..." do Post.published.proxy_options.should == {:conditions => {:published => true}} end
I find best approach (IMHO) in "Rail Test Prescriptions":
should_match_find_method :active_only { :active == true }
where
should_match_find_method
custom helper method -
jaydel almost 13 yearsAre you sure that querying in unnecessary in a model spec? I can understand not doing it in a controller or view spec--mock & stub it away. But I would think that you want to test the model and its relationship to the database here.
-
Jan Minárik almost 13 yearsIMO it is unnecessary when testing named scopes. You should have plenty of other tests that actually read from the database.
-
Rob Davis almost 13 yearsScopes are one area where it certainly makes sense to query the database to fetch real objects. I favor mocking where it's appropriate, but only the simplest scopes will work with mocks. Once you start joining or grouping, they'll break down, and I've made it a rule to test scopes against a real backing store. Configuration-based matchers like the second and third examples are error-prone, IMHO. That is, they're very likely to have the same errors as your code.
-
Chuck Vose over 12 yearsModels get factories, controllers get mocks. In the controller we care about our statuses and responses, in models we care about data. It's true that we shouldn't test baked in things like find, but a scope seems perfectly a perfectly rational thing to test.
-
Sam Peacey almost 12 yearsHe mentions nothing about scopes in that post however, why do you lump them in with associations? Scopes can be far more complicated and error prone than associations, and I believe should be tested. Since you invoked the mighty creator, here he suggests that if a scope falls under the umbrella of behaviour (and if it doesn't, why is it there?), then it should be specified.
-
Joost Baaij almost 12 yearsI am not disagreeing that scopes shouldn't be tested. They definitely should. Just not directly; the testing of scopes should be driven by testing the behaviour of the app. I see scopes as similar to associations, since both can have complex options like joins, included models, where/group by/having etcetera. In any case, this is what makes sense to me.
-
steve about 3 yearsI think when you have logic that builds scopes, particularly if that logic becomes complex, it can be useful to write unit tests around it.