RoR testing controllers - ruby-on-rails

I use RoR 3 and i guess something changed in controller's tests.
There is no
def test_should_create_post
but
test "should create user" do
...
end
Is there any decription how is that mapping etc? Because i dont get it.
And second thing. How to program (what assertion) use to test login?

so the test "something here" style is rails way of helping us out. It is fundamentally the same as def test_as_you_want but they helped us out by taking away those nasty '_(underscores)' and wrapping the actual test wording in a string. This change came back, phew... maybe 2.3.x. that fact has to be checked but at least a year and a half ago.
Your second thing is a little more harder to answer man. What plugin are you using, or are you one of those guys who are writing their own auth system?
Either way, check out how the 'famous' auth plugins do it. from Restful Auth to Devise, basically you want test that you can:
Signup for the User account
all of your confirmation emails are sent etc..
Most of these 'cheat' or take the easy way out by passing a helper called signed_in users(:one) for instance. Assuming you are cool and using fixtures.
Basically here is what a helper method looks like if your Auth plugin/gem doesn't have one, like Clearance which didn't have it when i was first writing my tests... not sure if it has it now but it sheds light on how it should look. Notice I've commented out Restful Auth and how he/they did it:
#login user
def login_user(user = users(:one))
#Restful Auth Example
# #request.session[:user_id] = user ? users(user).id : nil
# Clearance
#controller.class_eval { attr_accessor :current_user }
#controller.current_user = user
return user
end
Actually i think i stole this from their shoulda login helper... that's probably what i did. Either way it shows you how to fake login a user.
Now when you are testing, just pass this login_user method to your test when you need a user logged in and start testing the rest of the method without worrying about them actually signing in. That is what the plugin is supposed to do and the 1000 people following it on github would scream if it didn't at least LOG that guy in.
cheers

Related

Emailing Current User in ActionMailer - Rails 5

I have a Ruby on Rails application with multiple environments (development, staging, production). In my staging environment, I want to be able to override all emails being sent from the system to go to the current_user logged in. Everything I've read is telling me I have to keep passing in current_user into my mailer. However, this doesn't seem logical for me as there are hundreds of mailer code to change.
What I'd ideally like to do is to setup an email intercepter, override the mail.to and always send to current_user. Is there a way to do this? Here's what I have so far in my intializers:
if Rails.env.staging?
class OverrideMailRecipient
def self.delivering_email(mail)
mail.to = ['development#xxx.com']
end
end
ActionMailer::Base.register_interceptor(OverrideMailRecipient)
end
This works, but now I'd like to make it so it goes to current user instead of a hard-coded email.
As a bonus, I'd like add to the body of the email the original recipients of the email, so the current user knows who was supposed to receive it in production.
I hope this makes sense, any help is appreciated! :)
I'm using Rails 5.1.4 and Devise for authentication.
The only way I see is to add the current user to some global variable, then you would be able to access it from the Interceptor. Here is an option: https://stackoverflow.com/a/2513456/740394
To add any information you want to the mailers, use a partial with a condition in your layout like you did for the interceptor.
render('sender_info') if Rails.env.staging?

Rails: Cucumber and Application Controller Methods

I am doing integration testing using Cucumber. In my ApplicationController, I have a method called current_user that provides the current user object. I use this object to add items to a redis database:
$redis.sadd("cart#current_user.id}", [1,5,2])
In my Cucumber steps I test this functionality:
Then /^the redis database should have "(.+)" item ids/ do |count|
expect($redis.smembers("cart#{current_user.id}").count).to eq count.to_i
end
However, it is my understanding that Cucumber does not have access to controller methods, even if they are under ApplicationController, and therefore I cannot user the current_user method the way I would in my controllers.
What I am doing now is since I am testing features, there is only one user in the database so the current_user.id will always be 1, but if I start adding more users this may not work nicely.
Is there a workaround for this?
Your not really using Cucumber as intended here. What you are doing is testing how your application currently works, but really Cukes is best used to specify what your application does and why its important.
Applying more appropriate usage to your current problem leads to the following questions
What is the reason for storing the ids in Redis?
What benefit does the customer get by having these id's stored?
Taking a wild guess you might be saving a basket so that if the user logs out, their basket would still be populated when they come back. Then your scenario would be something like
Scenario: Remember products in basket
Given I am registered
And I am logged in
When I put some products in my basket
And I log out
And I log in again
Then my basket should still have some products in it
Notice how the scenario is all about WHAT you are doing and WHY its important but reveals nothing about HOW this is going to be done. This is a really good way to critique scenarios. Scenarios that contain HOW stuff are going to be harder to write and much harder to maintain. Anyhow enough of that :)
Now you can use standard cucumber stuff like assigned the user to a variable in one step e.g. #i = create_registered_user and then using that user in the other steps e.g. login as: #i
Note that we don't look at the database, only at what the user sees, and we don't reveal anything about HOW this functionality works in the scenario.
If you want to write tests (rather than scenarios) that do reveal how functionality works and do look at databases for results then I'd suggest that rspec would be better suited for this.
do you have a step to login? if so, you can change it a little so you can control which user logs in:
Given "john_doe" logs in to the app
Then you can search by username and do the login in your step. You can do the same on this step:
Then /^the redis database should have "(.+)" item ids/ do |count|
something like
Then /^the redis database should have "(.+)" item ids for user "(.*)"/ do |count, user_name|
user = User.find_by(username: user_name)
expect($redis.smembers("cart#{user.id}").count).to eq count.to_i
end

Newbie with Rails devise and view of the user

I'm looking into RoR some way to: login into the system with DEVISE, (it's working), but i'm needing something than keeps always the view of this logged user, and avoid than this user looks another views.
http://xx.xx.xx.xx:3000/user/1
And this user cannot look the content of:
http://xx.xx.xx.xx:3000/user/2.
Please, sorry if this is a silly question, but, i was looking 2 days and i don't know how i can name this feature.
Thanks!
There are gems available for this Authorization. I prefer can can which is one of the best Authorization gems available
Here is the gem=> https://github.com/ryanb/cancan
And here is the rails cast tutorial using it=> http://railscasts.com/episodes/192-authorization-with-cancan
EDIT: If you want to manually implement this then you just need to make a method with following logic
def check_authorization
# Assuming user ID is coming in params[:id]
if current_user.id == params[:id]
return
else
# render or redirect to some page with access denied message
end
end
And call this method just before any action in which you want to check for authorization.

Steps to create my own authentication system, need some guidance

I want to learn how to create my own authentication system, please provide some guidance if am doing this wrong.
I will create a Module in my /lib folder /lib/auth.rb
I will require this module in my ApplicationController.
when a user enters their email + password, I will call a method that will do a lookup in the user's table for a user with the same email, I will then compare the passwords. (i'll add encryption with salt later).
If the user entered the correct credentials, I will create a row in the Sessions table, and then write the session GUID to a cookie.
Now whenever I need to check if the user is logged in, or I need the user object, I will check if the cookie exists, if it does, I will lookup the session table for a row with the same guid, if it exists, I will return the session row and then load the User object.
I realize there are many suggestions one can give, but in a nutshell does this sound like a workable solution?
Now to make this usable, I will have to make some helper methods in my ApplicationController right?
How will I access the current_user from within my views?
P.S I know of other authentication systems, I just want to learn how to create my own.
The basic logic you're following is correct. Of course you can always expand on this with features that you need. For instance, you'll need helper methods for things like "logged_in?" and "current_user". Also, you might want to add session expiry, or session retention as a "remember me" feature.
Go for it, you won't learn authentication systems better than building your own then figuring what's wrong with it.
You should really check out the authlogic gem on github.
http://github.com/binarylogic/authlogic
It also has great instructions on how to set up your users.
After Faisal said what I would say, I only give you answer to the last part of your question:
"How will I access the current_user from within my views?"
try something like this:
class User < ...
def self.current=(u)
#current = u
end
def self.current
#current
end
end
In your views (or any part of your code) you can call User.current. Your controller has to assign a validated user to User.current. Your filters can react to "if User.current.nil?" and so on.
If you want to be thread safe, you may use a thread variable instead of #current:
Thread.current[:current_user] = u

Ruby on Rails functional testing with the RESTful Authentication plugin

I started writing functional tests for my rails app today. I use the RESTful authentication plugin. I ran into a couple confusing things I hope someone can clarify for me.
1) I wrote a quick login function because most of the functions in my rails app require authentication.
def login_as(user)
#request.session[:user_id] = user ? user.id : nil
end
The issue I see with this function, is it basically fakes authentication. Should I be worried about this? Maybe it is okay to go this route as long as I test the true authentication method somewhere. Or maybe this is terrible practice.
2) The second confusing thing is that in some places in my functional tests, I need the full authentication process to happen. When a user is activated, I have the do_activate method create some initial objects for the user. It is analogous to the creation of a blank notebook object and pen object for a student application, if that makes sense.
So in order to properly test my application, I need the user to hit that activation state so those objects are created. I am currently using Factory Girl to create the user, and then calling the login_as function above to fake authentication.
I guess another option would be to skip the full authentication sequence and just create the blank objects with Factory Girl. I could test the proper authentication somewhere else.
What do you think? If I should go through the proper sequence, why isn't the code below invoking the do_activate function?
user = Factory.create(:user)
user.active = 1
user.save
Thank you!
Faking it is perfectly acceptable.
However, write other tests that ensure that the things you want protected are protected. So
test "it should show the profile page" do
user = Factory(:user)
login_as(user)
get :show, :id => user
assert_response :success
end
test "it should not show the profile page cos I'm not logged in" do
user = Factory(:user)
get :show, :id => user
assert_response :redirect
end
Feel free to hit me up for followups!

Resources