Alternatives to find_by for ROR - ruby-on-rails

I'm working through Michael Hartel's rails tutorial, on 6.3 and need alternative code for the user_spec model. The code that he has is:
let(:found_user) { User.find_by(email: #user.email) }
It looks like I can use where, but unsure of the correct syntax. I tried several variations of the following:
let(:found_user) { User.where(:email => "#user.email")}
I'm sure this is a pretty easy answer, but cant quite get it.

let(:found_user){User.where(email: #user.email).first}
or
let(:found_user){User.find_by_email(#user.email)}
That first one that uses where returns a collection of users that match the where clauses, which is why you would need that .first (It doesn't execute the sql until you grab the records with something like .all, .first, or .each).
I would say it's not the best practice to execute database commands in a unit test though. What are you testing specifically? Is there a reason you need the user to be saved in the database and can't just do something like:
let(:user){User.new(email: 'some email')}

ActiveRecord::Base#find_by is effectively where(options).first, but that's a whole extra call that you needn't make.
Rails also provides mildly deprecated "magic" find_by_<attribute>[and_<attribute>] methods which used method_missing to parse out what was meant based on the name of the method. While the framework does provide these, I caution against using them as they are necessarily slower than "native" methods, and are more resistant to refactoring.
I would recommend sticking with find_by for the general case, and would try to avoid hitting the database in specs and tests.
The factory_girl gem provides a method to create a stubbed version of the class which quacks like a record returned from the database by answering true for persisted? and providing an id.
Alternatively, you can just build a new record without saving it: User.new(attribute: value, ...) and run your tests on that:
it "does some things" do
user = User.new(attributes)
# make user do some things
expect(things).to have_happened
end

Related

How do I expect a method to be run with specific ActiveRecord parameters

Using Mocha on Rails 4.2.
I'm testing a method that it should make a call to another method with the correct parameters. These parameters are ActiveRecord objects that it calls up from the database. Here is the key line in my test:
UserMailer.expects(:prompt_champion).with(users(:emma), [[language, 31.days.ago]]).once
Both users(:emma) and language are ActiveRecord objects.
Even though the correct call is made, the test fails because the parameters don't match the expectations. I think this might be because it's a different Ruby object each time a record is pulled up from the database.
I think one way around it is to see what method is being used in my code to pull up the records and stub that method to return mocks, but I don't want to do this because a whole bunch of Records are retrieved then filtered down to get to the right one, mocking all those records would make the test way too complex.
Is there a better way of doing this?
You could use block form of allow/expect.
expect(UserMailer).to receive(:prompt_champion) do |user, date|
expect(user.name).to eq "Emma"
expect(date).to eq 31.days.ago # or whatever
end
Sergio gave the best answer and I accepted it. I discovered the answer independently and found out along the way that I needed to return a mock from the ActionMailer method to make everything work properly.
I think it best to post here my complete test here for the sake of any other hapless adventurer to come this way. I'm using Minitest-Spec.
it 'prompts champions when there have been no edits for over a month' do
language.updated_at = 31.days.ago
language.champion = users(:emma)
language.save
mail = mock()
mail.stubs(:deliver_now).returns(true)
UserMailer.expects(:prompt_champion).with do |user, languages|
_(user.id).must_equal language.champion_id
_(languages.first.first.id).must_equal language.id
end.once.returns(mail)
Language.prompt_champions
end
You could use an RSpec custom matcher and compare expected values in that function.

Rails common method for updating a database field

I am new to rails and I have a task to write a common method that will update a specific database field with a given value. And I should be able to invoke the method from anywhere in the app.(I understand about the security flaw and so on.. But I was asked to do it anyway) In my application controller I tried
def update_my_model_status(model,id,field, value)
#model = model.find(id)
#model.update(field: value)
end
Of course this doesn't work.. How to achieve this? What is the right way to do this? And if it is possible how to pass a model as an argument to a method?
If you're using Rails, why not use Rails?
Compare update_all:
MyModel.where(id: 1).update_all(banned: true)
or maybe update_attribute:
my_model.update_attribute(:banned, true)
to:
update_my_model_status(MyModel, 1, :banned, true)
Notice how, despite being shorter, the first two approaches are significantly more expressive than the last - it is much more obvious what is happening. Not only that, but they are immediately more familiar to any Rails developer off the street, while the custom one has a learning curve. This, combined with the added code from the unnecessary method adds to the maintenance cost of the application. Additionally, the Rails methods are well tested and documented - are you planning to write that, too? Finally, the Rails methods are better thought out - for example, your prototype naively uses attribute validations, but does not check them (which could result in unexpected behavior) and makes more SQL queries than it needs to. It's fine to write custom methods, but let's not write arbitrary wrappers around perfectly fine Rails methods...
Try this:
def update_my_model_status(model,id,field, value)
#model_var = model.capitalize.constantize.find(id)
#model_var.update_attributes(field: value)
end
Instead of just using update you should use update_attributes:
def update_my_model_status(model,id,field, value)
#model_var = model.find(id)
#model.update_attributes(field: value)
end
http://api.rubyonrails.org/classes/ActiveRecord/Persistence.html#method-i-update

Rails –Testing named scopes: test scope results or scope configuration?

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:
it's making a black-box test grey(er)
I have to make the assumption that because something is configured a certain way, I'll get the results that my business logic requires
The example I've used is so trivial that perhaps I'd be okay with just testing the configuration, but:
where do you draw the line and say 'the content of this named scope is too complex and requires result confirmation tests over and above just scope configuration testing'? Does that line even exist or should it?
Is there a legitimate/well-accepted/'best practice' (sorry) way to test named scopes without touching the database, or at least touching it minimally, or is it just unavoidable?
Do you use either of the above ways to test your scopes, or some other method entirely?
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.
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...

How to write Rspec spec for the following query

Hi I have the following query in my controller and I want to write the Rspec spec . I am new to Rspec and I don't know how to write the spec. Kindly help
table1.includes(:table2).where(table1: {id: params[:id]}).includes(:table3)
I also tried looking into mocks and stubs but i don't understand how to use them for a query like this.
Thanks
When faced with these issues, I tend to encapsulate the query in a method. That way, you can stub out the method with data simply and without worrying about data-sanitation.
For example:
def fetch_table1_results(id)
table1.includes(:table2).where(table1: {id: id}).includes(:table3)
end
At this point, you can stub out the method when you need to test things that depend on it:
awesome_model = stub_model(Table1, fetch_table1_results: [1, 2, 'etc']) # You should include models, stubs, or mocks here.
As far as testing the actual method, I'm not sure you need to. There aren't many interesting parts of that method chain. If you wanted to be complete, here are the cases:
Ensure fetch_table1_results calls any instance of Table1.find with id
Ensure fetch_table1_results eager-loads table2 and table3
The way of doing the latter varies, but I'm rather fond (and this won't be a popular opinion) of checking the database query directly. So you could type something like the following:
fetch_table1_results(1).to_sql.should include('JOIN table2')
That, or something similar. I should also note that these tests should be in the model, not the controller.

How to spec operations that rely on Memcached?

We have a Rails application that we test with RSpec. We want to spec operations that rely on Memcached. What is the best practice to do so?
I thought of doing this by stubbing all calls to Rails.cache. Is this a good idea?
As per #Pan Thomakos suggestion, I'm adding some additional details about one of the scenarios I'm trying to test:
We have the concept of accounts in our system, therefore on every request we retrieve the current user and the current account. Because there are not many accounts in the system, we keep them all in cache and retrieve them from there.
def self.find_by_slug(slug)
Rails.cache.fetch(Account.cache_key_for_slug(slug), :expires_in => 1.day) { super }
end
For this reason, caching in this case isn't just a nice to have behavior, but the expected behavior and the thing I want to test. Therefore turning off caching won't do.
Test without stubbing IMHO!
The sequence would look like this:
Cache.flush # or equivalent
Cache.get(slug).shouldbe null # test cache is empty
Method.find_by_slug(slug).should == 'some value' # test that method words
Cache.get(slug).should == 'some value' # test that cache has value.
Personally, I believe if you have the resources on hand then stubbing SHOULD NOT be used. If you do not have the resources on hand (IE a 3rd party service) then stubbing SHOULD BE used.
The problem with stubbing, is that if you changed the code that you are stubbing, then you won't know if it breaks.
An example in this case would be if you switched from the standard memcache gem, to Dahli?, or some other memcache gem which handed cache misses by returning false, null or some other value differently. I mean really! Cache.set("my_key", false)! :)
A example for switching, would be to leave the ASCII protocol and move to the faster binary protocol.
Memcache is a cheap resource, you can set it up with 1 meg of ram to do this testing. I would even go as far as to say you can do the same for mysql. Anything bigger than mysql, then I would start leaning towards stubbing as the cost to "setup" those resources becomes significant. YMMV.
-daniel
It seems like if you're using Rails.cache.fetch directly your best option is to stub. But if you use the controller helpers (which are now in seperate gems in rails 4), I came across this gem which is helpful https://github.com/avit/rspec-rails-caching

Resources