How should Rails named scopes be tested? Do you test the results returned from a scope, or that your query is configured correctly?
If I have a User
class with an .admins
method like:
class User < ActiveRecord::Base def self.admins where(admin: true) end end
I would probably spec to ensure I get the results I expect:
describe '.admins' do let(:admin) { create(:user, admin: true) } let(:non_admin) { create(:user, admin: false) } let(:admins) { User.admins } it 'returns admin users' do expect(admins).to include(admin) expect(admins).to_not include(non_admin) end end
I know that this incurs hits to the database, but I didn't really see any other choice if I wanted to test the scope's behaviour.
However, recently I've seen scopes being specced by confirming that they're configured correctly, rather than on the result set returned. For this example, something like:
describe '.admins' do let(:query) { User.admins } let(:filter) { query.where_values_hash.symbolize_keys } let(:admin_filter) { { admin: true } } it 'filters for admin users' do expect(filter).to eq(admin_filter) # or some other similar assertion end end
Testing the direct innards of a query like this hadn't really occurred to me before, and on face value it is appealing to me since it doesn't touch the database, so no speed hit incurred.
However, it makes me uneasy because:
The example I've used is so trivial that perhaps I'd be okay with just testing the configuration, but:
This question(s) is a bit similar to Testing named scopes with RSpec, but I couldn't seem to find answers/opinions about testing scope results vs scope configuration.
Scopes are custom queries that you define inside your Rails models with the scope method. Every scope takes two arguments: A name, which you use to call this scope in your code. A lambda, which implements the query.
Scopes are used to assign complex ActiveRecord queries into customized methods using Ruby on Rails. Inside your models, you can define a scope as a new method that returns a lambda function for calling queries you're probably used to using inside your controllers.
Scope defines where in a program a variable is accessible. Ruby has four types of variable scope, local, global, instance and class. In addition, Ruby has one constant type. Each variable type is declared by using a special character at the start of the variable name as outlined in the following table.
I think you have described the problem very well, and that the best answer, in my opinion is - it depends.
If your scope is trivial, run-of-the-mill where
, with some order
, etc. there is no real need to test ActiveRecord
or the database to make sure they work properly - you can safely assume that they have been correctly implemented, and simply test the structure you expect.
If, on the other hand, your scope (or any query) is compound, or uses advanced features in a complex configuration, I believe that setting up tests that assert its behavior, by using a real live database (which is installed locally, with a small custom-tailored data set) can go a long way in assuring you that your code works.
It will also help you, if and when you decide to change strategies (use that cool new mysql
feature, or porting to postgresql
), to refactor safely, by checking that the functionality is robust.
This is a much better way than to simply verify the the SQL
string is what you typed there...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With