Ruby on Rails functional testing with the RESTful Authentication plugin - ruby-on-rails

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!

Related

RSpec - How to test the if-else block in update method of controller

Let's say I have a database to store the data of people (name, email). And there's an update method in the controller. The method is like:
def update
#people = People.find(params[:id])
if #people.update(people_params)
redirect_to people_path
else
render 'edit'
end
end
I wonder how can I test the situation that the update failed? Or do I really need to test it? I have searched it on StackOverflow, there is a link about it, but it not says if I should or should not to test it. Could anyone give me some help about it? If it can be test, how? Thank you so much!
You don’t need to test Ruby and Rails internals. Either you trust both of them do work as expected, or you’d better switch to some other language / framework.
Whether you still want to test it, mock everything unrelated. Here is an example of doing this with rspec and flexmock.
describe '#update' do
let(:person) { build(:people) }
before do
flexmock(People).should_receive(:find).once.returns person
flexmock(person).should_receive(:update).once.returns false
end
it 'redirects to `edit` page' do
...
end
end
Typically the update failure would be due to object to be saved not being valid, according to its validation criteria. You would normally test the validation criteria in the model specs, but when testing the controller, or in integration tests, you might want to ensure that the #edit render occurs when the user tries to save an invalid model.
If the model does not have any validation criteria, you can probably skip the else render :edit altogether. It's part of the Rails scaffold that may not apply in all cases.
You can test your update fail scenario by trying to save an invalid model. You would typically confirm that the user was correctly informed of the validity problem (flash message).
There's no right or wrong as to whether or not to test. Personally, I would test it, b/c I like TDD, and I prefer to over-test rather than under-test. Many would not.

What tests should I write to test user validations for an admin-only feature?

I created an RSpec spec to test if a POST #create action works properly:
describe "POST #create" do
it "creates a career" do
expect {
post "/careers/", career: attributes_for(:career)
}.to change(Career, :count).by 1
end
end
The above code works correctly. The issue happens when I create another test to allow only users with roles of "admin". Do I need to create a new user, log them in, and then run the above test? Do I need to do this for all future tests which have a restriction based on the user's role?
Is there another way to do this type of testing? 1) Just test if the create method works, and 2) only allow Users with "admin" role access the GET #new and POST #create methods?
When your feature is fully developed you'll want to have the following tests:
one happy-path test in which an admin creates a career
one in which a non-admin tries to create a career but is prevented from doing so
possibly another in which a not-logged-in user tries to create a career but is prevented from doing so (whether you want this depends on whether you have to write different code to handle non-logged-in and logged-in non-admin users), and
possibly other tests of different scenarios in which an admin creates a career.
This idea of having one complete, happy-path test is one of the most fundamental patterns in testing, but I'm not aware that it has a name, other than being implied by the term "happy path".
It looks like you're doing TDD. Great! To get from where you are now to the above list of tests, the next test to write is the one where the non-logged-in user is prevented from creating a career. To make both tests pass at the same time you'll need to change the first test to log in an admin. And if you need more tests of successfully creating a career (bullet 4), yes, you'll need to log in an admin in those too.
Side notes:
Unless you already have it, I'd write your happy-path spec not as a controller spec but as a feature spec (an acceptance test), so that you specify the important parts of the UI and integration-test the entire stack. Your failed-authentication specs might work as controller specs, although you might decide you need to acceptance-test the UI when a user doesn't have permissions for at least one of those scenarios.
I really don't like that expect {}.to change syntax. It prevents you from making any other expectations on the result of posting. In your example I would want to expect that the HTTP response status is 200 (response.should be_success). As I said, though, my first spec would be a feature spec, not a controller spec.
So, this is an interesting question. Yes, you should definitely (IMO) test authentication separately from the target method/action. Each of these constitutes a unit of functionality and should be tested as such.
In my current project, I'm favoring POROs (I often keep them in a directory called 'managers' although I know many people prefer to call them 'services') for all sorts of things because it lets me isolate functionality and test it independently. So, I might end up with something like:
# controllers/foo_controller.rb
class FooController < ApplicationController
before_action :authenticate
def create
#results = FooManager.create(params)
redirect_to (#results[:success] ? my_happy_path : my_sad_path)
end
def authenticate
redirect_to unauthorized_path unless AuthenticationManager.authenticate(params, request)
end
end
# managers/foo_manager.rb
class FooManager
class << self
def create(params)
# do a bunch of great stuff and return a hash (perhaps a
# HashWithIndifferentAccess, if you like) which will
# allow for evaluation of #results[:success] back in the
# controller.
end
end
end
# managers/authentication_manager.rb
class AuthenticationManager
class << self
def authenticate(params, request)
# do a bunch of great stuff and return a boolean
end
end
end
With an approach like this, I can very easily test FooManager.create and AuthenticationManager.authenticate (as well as FooController.create and FooController.authenticate routing) all independently. Hooray!
Now, whether your authentication framework or controller method is behaving correctly at a unit level, as Dave points out very well, is a separate issue from whether your entire system is behaving as expected. I'm with him on having high-level integration tests so you're clear about what 'done' looks like and you know when to holler 'ship it!'

Is there a better way to test :admin security

I am going through Hartl's Rails Tutorial. I'm up to the first exercise of 9.6, where he asks me to test that the User admin attribute isn't accessible. The justification is earlier in the book:
After Listing 9.42, Hartl's Rails Tutorial says
If we omitted the attr_accessible list in the User model (or foolishly added :admin to the list), a malicious user could send a PUT request as follows:
put /users/17?admin=1
The corresponding exercise (exercise 9.6.1) in the tutorial says
add a test to verify that the User admin attribute isn’t accessible
I have completed that test with this code in user_spec.rb:
expect do
#user.update_attributes(:admin => true)
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
But I used stackoverflow to get that test. This was my original idea (in user_pages_spec.rb):
expect do
put user_path(user) + "?admin=1"
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error) # or some other error
But I couldn't get it to work.
So my questions are:
Is my idea possible? Isn't it better to test directly for what a potential hacker might do from the command line? Isn't that the idea of Capybara, testing user actions?
If it is possible, is there a difference between testing mass assignment and testing the PUT action?
If it isn't possible, why? Is it just not necessary or am I missing something here?
I think I would argue with you that your test is actually better. Some would argue that the given answer is testing Rails functionality which really isn't your job. However, I do think it's frequently good to test things in several different directions.
I was under the impression from back in my school days that it was impossible to send data via the URI except when doing a GET. A quick search of stackoverflow didn't result in any confirmation. However, the wikipedia article seems to imply it:
http://en.wikipedia.org/wiki/POST_%28HTTP%2
I think the correct line of code would be
put user_path(user), {user: {admin: 1}, id: user.id}
I hope that helps.

Cucumber and Clearance: current_user in steps

What I what to accomplish is to use (rely on) current_user method while defining Cucumber steps. I'm using Clearance in my project.
First of all I tried to use sign_in but it didn't work (I guess Cucumber World doesn't know about Clearance methods...).
So how do I make Cuckes recognize current_user and sign_in/sign_out methods?
Your Cucumber features should be driving your application through the public user interface. Something like:
Given /^I am signed in as "([^\"]*)"%/ do |username|
visit 'sign_in'
fill_in 'Username', :with => username
click 'Sign In'
end
Since the current_user method isn't available to the browser, you shouldn't be using it in your spec.
You could fake it in your steps by storing #current_user in the above step and then providing an attribute reader for it.
I disagree with the idea that every acceptance test (cucumber or otherwise) must exercise the login logic. Luckily, if you agree, Clearance has added a back door in tests that lets you skip the sign in steps.
user = create(:user)
visit posts_path(as: user)
Now you can leave your login-related features driving the login ui as a user would and skip that for features that aren't directly related to logging in.

RoR testing controllers

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

Resources