undefined local variable or method `request' - ruby-on-rails

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

Related

rspec, request spec, devise, multi-controller

I'm trying to build a request spec which tests the full gamut of creating a new user via devise API.
I currently have this sitting in RegistrationsController spec, but this won't work if I want to follow the mail link to the confirmations controller.
I haven't been able to find a good example of how people have tested the 'hand-off' from one controller to another and the intermittent 'steps' (we have custom devise methods scattered throughout the process which this test will encompass too).
it "creates a user, sends a welcome email, confirms the user, and registers the user in email campaigns" do
post :create, {user: new_user_params}
last_email = ActionMailer::Base.deliveries.last.body
ConfirmationsController.any_instance.should_receive(:after_filter_method_to_subscribe_user)
redirect_to confirmation_link(last_email) # helper method
last_email.should include("Thanks for joining!")
user = User.find_by_first_name(new_first_name)
user.confirmed?.should be_true
user.email_lists.should_not be_empty
end
Edit:
I should also add that I need http_basic_auth to run the spec which I'm including in a spec/support file and sets the request.env['HTTP_AUTHORIZATION'] to variables stored in the API::Base controller. I currently have nil as a request obect when running specs in the spec/request folder, which I'll need to run the specs.
Edit:
Thanks to people who've taken a look. I figured it out after piecing together two SO searches and the code I had. I'll post an answer for future SO'ers when I can.
I figured this out shortly after posting my question with good luck finds on more google searches. Kudos to a couple SO references ~> request spec relish: http://goo.gl/iBg7v1 && setting request headers for http basic auth in request specs: http://goo.gl/hdDBMd
My spec turned out to look something like the below Hope this helps someone not waste 4 hours like me :).
spec/requests/api/user_registration_spec.rb.
it "sends a welcome email, confirms the user, and signs the user up to email campaigns" do
email_list = FactoryGirl.create(:email_list, name: "funky-email-campaign")
user_name = Api::RegistrationsController::USER
password = Api::RegistrationsController::PASSWORD
# post to /users/registration
post api_registrations_path({user: new_user_params}), nil , {'HTTP_AUTHORIZATION' => ActionController::HttpAuthentication::Basic.encode_credentials(user_name, password)}
last_email = ActionMailer::Base.deliveries.last.body
UserService.should_receive(:subscribe_to_email).and_call_original # check that after_filter is called
get confirmation_link(last_email) # follow link in email (/users/confirmation)
response.should redirect_to(custom_path) # tests after_confirmation_path_for override
last_email.should include(new_first_name)
last_email.should include("Thanks for joining!")
user = User.find_by_first_name(new_first_name)
user.confirmed?.should be_true
user.email_lists.first.name.should eq(email_list.name)
end

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.

separate helper function to log in before every rspec request test is run

I've been struggling with creating a login function that should be executed before any rspec test is run.
What I have right now is:
def login
post "/session", { "session[username]" => "bjones" }
end
in my spec_helper.rb file
Then, I have the following in one of my spec.rb files in the requests directory.
require 'spec_helper'
describe "Sessions" do
describe "GET /dashboards" do
login
it "displays the dashboard" do
get dashboard_path
puts response.body
end
end
end
However, when I try running the test, I get:
undefined method `post' for #<Class:0x4f08828> (NoMethodError)
I'm pretty new to rails and completely new to testing and rspec so maybe there's something fundamental I'm missing here. Basically, all I want to do is set that session variable so that when the test is run I will be logged in. Perhaps a different approach would be better? Or maybe I need to put that login function in a different place?
I came across this answer which was sort of useful but it's not for rspec so I'm having trouble understanding where such a helper function would go.
Try
let(:login) {
post "/session", { "username" => "bjones" }.to_json
}
This might have to be revised to use .to_json or not, depending on what content type the controller accepts.

Help with a Given step that goes to a page and as a logged in user

My scenerio looks like:
Given I am on the homepage
As a member
When I follow "new post"
..
..
In my web_steps.rb I added:
When /^As a (.+)$/ do |type|
#user = Factory(:user, type)
end
My factories are in:
/spec/factories.rb
/spec/factories/user.rb
How do I reference my factories.rb into my web_steps.rb page?
Am I doing this correctly? Now this instance variable #user is the variable that my controller checks to see if it is authenticated correct?
I'm getting this error currently:
features/create_post.feature: Lexing error on line 8: ' As a member'.
What is wrong with my 'As a member' line?
Update
I noticed I had:
When /^As a
I changed it to:
As /^a
now I get:
undefined method `As' for main:Object (NoMethodError)
'As' is not valid in a cucumber scenario as far as I know.
Your lines should start with 'Given', 'When', 'Then', or 'And'.
Your scenario should probably look more like
Given I am logged in as 'User'
When I go to the homepage
And I follow "New post"
Then ...
Rather than write web steps that instantiate factories, i use Pickle for this. Pickle is nice, because you don't have to futz with your factories at all in cucumber. It defines generic steps for creating any given factory object (model), as well as steps for finding model objects and other things.
Ryan Bates has a created a screenscast on pickle as well. Rails screencasts are a nice post-lunch office tradition around our place!
Ok, good luck,
ian.

Stubbing named_scope in an RSpec Controller

I haven't been able to find anything for a situation like this. I have a model which has a named scope defined thusly:
class Customer < ActiveRecord::Base
# ...
named_scope :active_customers, :conditions => { :active => true }
end
and I'm trying to stub it out in my Controller spec:
# spec/customers_controller_spec.rb
describe CustomersController do
before(:each) do
Customer.stub_chain(:active_customers).and_return(#customers = mock([Customer]))
end
it "should retrieve a list of all customers" do
get :index
response.should be_success
Customer.should_receive(:active_customers).and_return(#customers)
end
end
This is not working and is failing, saying that Customer expects active_customers but received it 0 times. In my actual controller for the Index action I have #customers = Customer.active_customers. What am I missing to get this to work? Sadly, I'm finding that it's easier to just write the code than it is to think of a test/spec and write that since I know what the spec is describing, just not how to tell RSpec what I want to do.
I think there's some confusion when it comes to stubs and message expectations. Message expectations are basically stubs, where you can set the desired canned response, but they also test for the call to be made by the code being tested. In contrast stubs are just canned responses to the method calls. But don't mix a stub with a message expectation on the same method and test or bad things will happen...
Back to your question, there are two things (or more?) that require spec'ing here:
That the CustomersController calls Customer#active_customers when you do a get on index. Doesn't really matter what Customer#active_customers returns in this spec.
That the active_customers named_scope does in fact return customers where the active field is true.
I think that you are trying to do number 1. If so, remove the whole stub and simply set the message expectation in your test:
describe CustomersController do
it "should be successful and call Customer#active_customers" do
Customer.should_receive(:active_customers)
get :index
response.should be_success
end
end
In the above spec you are not testing what it returns. That's OK since that is the intent of the spec (although your spec is too close to implementation as opposed to behavior, but that's a different topic). If you want the call to active_customers to return something in particular, go ahead and add .and_returns(#whatever) to that message expectation. The other part of the story is to test that active_customers works as expected (ie: a model spec that makes the actual call to the DB).
You should have the array around the mock if you want to test that you receive back an array of Customer records like so:
Customer.stub_chain(:active_customers).and_return(#customers = [mock(Customer)])
stub_chain has worked the best for me.
I have a controller calling
ExerciseLog.this_user(current_user).past.all
And I'm able to stub that like this
ExerciseLog.stub_chain(:this_user,:past).and_return(#exercise_logs = [mock(ExerciseLog),mock(ExerciseLog)])

Resources