Making functional tests in Rails with Devise - ruby-on-rails

After 3 years of procrastination today is the day that I start testing my Rails apps. My first step is to fix the failing tests in my Rails 3 beta4 app.
My last 3 failing tests have to do with the devise gem and its authenticate_user! method in a before_filter at the top of my controller.
You'd earn great karma by helping me out with this since it will enable me to use the TDD methodology from now on.
Here is the error that troubles me:
1) Error:
test_should_get_accepted(ModerationControllerTest):
NoMethodError: undefined method `authenticate!' for nil:NilClass
/test/functional/moderation_controller_test.rb:10:in `test_should_get_accepted'
Devise just gives functional tests pointers and helpers in this page: http://github.com/plataformatec/devise but I just don't know how to put this into application.
Can you please give this testing noob some detailed instructions on how to use these helpers?

It took me a while but I found the way. Here it is for anyone stuck at the same point:
At the top of the moderation_controller_test.rb, below the class declaration, add this line:
include Devise::TestHelpers
I have 2 records in my user fixture and I added this line within each test where the user has to be authorized to perform the action.
sign_in User.first
Of course it's dead simple once you know how to do it.

If you want the Devise test helpers to be available to all of your tests, you have to enclose the include mentioned by allesklar at the bottom of test_helper.rb in a class declaration like this:
class ActionController::TestCase
include Devise::TestHelpers
end
Update: 01.25.2017
... rails 5 posts a DEPRECATION WARNING & asks you use ...
Devise::Test::ControllerHelpers

I'm relatively new to Rails, so I'd like to add a couple things that may not be obvious to other new folks.
Concerning the user fixture, I had to define one but leave it empty in order for this to work:
# in users.yml
joe: {}
When using the devise sign_in helper, you can access the hash object directly in your test:
# a test method in some_controller_test.rb
sign_in users(:joe)
See http://guides.rubyonrails.org/testing.html#the-low-down-on-fixtures for more information on Rails fixtures.

Quoting verbatim from https://github.com/plataformatec/devise:
If you're using RSpec, you can put the following inside a file named spec/support/devise.rb:
RSpec.configure do |config|
config.include Devise::TestHelpers, :type => :controller
end
You can now use sign_in and sign_out in your RSpec tests.

In addition to the code in the test_helpers.rb, I added this at the top of the controller_test and it worked for me:
require 'test_helper'

Related

undefined local variable or method `request'

I am new to ruby on rails. I am getting an undefined method error when I run rspec on comment_spec.rb
1) after_save calls 'Post#update_rank' after save
Failure/Error: request.env["HTTP_REFERER"] = '/'
NameError:
undefined local variable or method `request' for #<RSpec::ExampleGroups::AfterSave:0x007fa866ead8d0>
# ./spec/models/vote_spec.rb:45:in `block (2 levels) in <top (required)>'
This is my spec:
require 'rails_helper'
describe Vote do
....
describe 'after_save' do
it "calls 'Post#update_rank' after save" do
request.env["HTTP_REFERER"] = '/'
#user = create(:user)
#post = create(:post, user: #user)
sign_in #user
vote = Vote.new(value:1, post: post)
expect(post). to receive(:update_rank)
vote.save
end
end
Any help that you would have would be greatly appreciated...
I was following the apirails book tutorial chapter 3 here
http://apionrails.icalialabs.com/book/chapter_three
I was receiving the same error and DrPositron's solution worked for me, all green again. Just needed to add ":type => :controller" on my block like so:
describe Api::V1::UsersController, :type => :controller do
end
Hope this helps someone
OK here's the deal.
Vote is a model, i suppose.
You are writing a test for that model.
There's a difference between model tests ("the domain logic is doing what its supposed to") and feature/integration tests ("the application is behaving the way its supposed to").
The request variable is associated with feature or controller tests.
So what's wrong?
You are not logging in users in model tests, just check if the update_rank method is being called on save, thats it.
No user-interaction jazz in model tests.
Hope that helps!
Cheers
Jan
So Louis, just to expand on Jan's response:
You appear to be writing a model spec. The purpose of a model spec is simply to test how your model classes work, and that behavior is testable without having to pay any attention to the application logic around signing in, making "requests" to particular controllers, or visiting particular pages.
You're essentially just testing a couple related Ruby classes. For this, we don't need to think about the whole app -- just the classes we're testing.
As a consequence, RSpec doesn't make certain methods available in the spec/models directory -- you're not supposed to think about requests or authentication in these tests.
It looks like your test is simply designed to make sure that when you create a vote for a post, it updates that post's rank (or, specifically, call's that post's update_rank method). To do that, you don't need to create a user, or sign a user in, or pay any attention to the request (what request would we be referring to? We're just testing this as if in Rails console, with no HTTP request involved).
So you could basically remove the first four lines of your test -- apart from the line creating your post, and the post's user if it's necessary (if the post model validates the presence of a user). Don't sign a user in -- we're just testing a Ruby class. There's no concept of a website to sign into in this test.
Then, as a last thing to take care of to get your spec to pass, make sure to refer to the post you create by the right name. Right now, you're creating a post and assigning it to the #post variable, but then you're referring to just post later on. post doesn't exist; just #post. You'll have to pick one variable name and stick with it.
Also, if you are using RSpec 3, file type inference is now disabled by default and must be opted in as described here. If you're new to RSpec, a quick overview of the canonical directory structure is here.
For example, for a controller spec for RelationshipsController, insert , :type => :controller as such:
describe RelationshipsController, :type => :controller do
#spec
end

How to test for mass assignment errors in Rspec and Rails 4?

I recently upgraded my Rails app from Rails 3 to 4 and this Rspec test is no longer passing:
# spec/models/user_spec.rb:
require 'spec_helper'
describe User do
it "should not allow access to admin" do
expect do
User.new(:admin => true)
end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
end
end
I am getting this error:
Failure/Error: end.to raise_error(ActiveModel::MassAssignmentSecurity::Error)
NameError: uninitialized constant ActiveModel::MassAssignmentSecurity
I suspect that this is somehow due to the switch to Rails 4's strong parameters.
How can I test for mass assignment errors now?
Thanks for any help.
As Baldrick pointed out rightly there's no need in Rails 4 to test for mass assignment issues in Rspec model tests. The whole idea of Rails 4's Strong Parameters is to move all that functionality to the controller.
To make your test pass you need add config.active_record.mass_assignment_sanitizer = :strict to
config/aplication.rb and add gem https://github.com/rails/protected_attributes to Gemfile.
If you want test strong parameters read this article http://pivotallabs.com/rails-4-testing-strong-parameters/

How can I access helper methods in my decorator spec files using Draper 0.14.0

Currently in my spec/decorators/product_decorator_spec.rb, I have the following:
require 'spec_helper'
describe ProductDecorator do
let(:product) { FactoryGirl.create(:product) }
subject do
ProductDecorator.first
end
before do
product
end
it 'should render the name attribute with a link to the product page' do
subject.name.should == h.link_to(product.name, 'test')
end
end
When I run my spec I get the following:
F.....
Failures:
1) ProductDecorator should render the name attribute with a link to the product page
Failure/Error: subject.name.should == h.link_to(product.name, 'resr')
NameError:
undefined local variable or method `h' for #<RSpec::Core::ExampleGroup::Nested_2:0x007fbbf212c8b0>
# ./spec/decorators/product_decorator_spec.rb:15:in `block (2 levels) in <top (required)>'
Finished in 0.98531 seconds
6 examples, 1 failure
Failed examples:
rspec ./spec/decorators/product_decorator_spec.rb:14 # ProductDecorator should render the name attribute with a link to the product page
According to the documentation, specs placed in the decorator folder should have access to the helper method, however my spec does not. I've also tried manually tagging my specs, but doesn't seem to have any effect.
Thanks for looking.
if you want to access the helper, you can do it via your_decorator.h.link_to.
when you are setting the subject, you will need to make sure that the thing you are calling will get routed to the helper, there is nothing injected into your rspec example!
in your example it would be subject.h.link_to for calling a helper method.
i also think that there are a lot of wired things in your spec. your usage of let, subject and before are kind of disturbing for me...
here is a nice writeup about how to write clean rspec: http://eggsonbread.com/2010/03/28/my-rspec-best-practices-and-tips/
I've encountered the same issue, where calling helper methods on the decorator's helper proxy (.h) doesn't work in test (in Draper 1.3). I ended up working around it with this, though I'm not very pleased with it:
my_decorated_object.h.extend ApplicationHelper
Your mileage may vary depending on how many controller features you access in your helper.

Skip Rails http_basic_authenticate_with in RSpec test

I'm working on a MVP (minimum viable product). In order to offer a simpler way to secure the admin pages, I just did add http_basic_authenticate_with to my AdminController.
Problem is that when I want to test my AdminController, I get "unauthorized" (401) for not being logged in.
In this scenario, it's irrelevant to test the authentication - it's just temporary, and as soon I go to the next sprint, it's going to removed -, so I'm trying to skip it within RSpec.
Problem is I tried many ways, and none seems to be working.
For example, I tried to modify the http_basic_authenticate_with in order to avoid the authentication. Like this:
require 'spec_helper'
module ActionController
module HttpAuthentication
module Basic
def http_basic_authenticate_with(*args)
end
end
end
end
describe Admin::SubscribersController do
describe "GET 'index'" do
it "should be OK" do
get 'index'
response.should be_successful
end
end
end
But when I run it, it still returns "false" for that simple test.
Btw, in order to simplify this test, I just have an empty index action on my AdminController and an empty view (index.html.erb).
Finally I got it working.
Something as stated in the docs didn't work for me:
get 'index', nil, 'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials("admin", "password")
So I tried an "old" approach to do it, which is to set request.env['HTTP_AUTHORIZATION'] :
request.env['HTTP_AUTHORIZATION'] = ActionController::HttpAuthentication::Basic.encode_credentials("admin","password")
None of the other solutions worked, so I will just keep in the meanwhile with this one.
Thanks.
If it is ok to skip authentication for all tests for a controller, here's the technique I'm using on a current project.
unless Rails.env.test?
http_basic_authenticate_with name: "slothbear", password: "kuniklo"
end
In Rails 5.x, this works:
allow(subject).to receive(:authenticate_or_request_with_http_basic)
.with(anything).and_return true
In Rails 6.x, this works:
allow(subject).to receive(:http_basic_authenticate_or_request_with)
.with(anything).and_return true
This is because http_basic_authenticate_with is a class-level method that adds a before_action that actually calls one of these two methods under the hood.
You can see which one to use by checking out http_authentication.rb here for Rails 6 or here for Rails 5
You can or even should test authentication. Write test for unauthenticated (it is now) and authenticated.
See Testing HTTP Basic Auth in Rails 2.2+ it should help.

How do you test whether a model has a given method in Rails?

I would like to implement the method User.calculate_hashed_password. I'm trying to use the Shoulda testing library which works with Rails's built-in testing tools, so an answer related to Test::Unit would be just as good as one related to Shoulda (I think).
I'm trying to figure out what I need to test and how I should test it. My initial idea is to do something like...
class UserTest < ActiveSupport::TestCase
should 'Return a hashed password'
assert_not_nil User.calculate_hashed_password
end
end
Is this the right way to do it?
You don't need to test that the method exists, just that the method behaves correctly. Say something like this:
class UserTest < ActiveSupport::TestCase
setup do
#user = User.new
end
should 'Calculate the hashed password correctly'
#user.password = "password"
#user.hashed_password = "xxxxx" # Manually calculate it
end
end
(I don't use shoulda, so excuse any glaring syntax errors.)
That test will fail if the method doesn't exist.
I agree with Otto; but as dylanfm noted, I use #respond_to to test for associations in RSpec.
it "should know about associated Projects" do
#user.should respond_to(:projects)
end
Maybe use respond_to?
You should check out Object#respond_to? and Object#try in newer versions of Rails. If you're new to testing in general, definitely read through this excellent guide on testing in Rails.

Resources