foo_url(mock_foo) sometimes doesn't work in rspec tests - ruby-on-rails

I am trying to write an rspec test for a controller that accesses a
model Group.
#request.env['HTTP_REFERER'] = group_url(#mock_group) ### Line 49
I get this:
NoMethodError in 'ActsController responding to create should redirect to :back'
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.rewrite
/Library/Ruby/Gems/1.8/gems/actionpack-2.1.0/lib/action_controller/base.rb:621:in `url_for'
(eval):17:in `group_url'
/Library/Ruby/Gems/1.8/gems/actionpack-2.1.0/lib/action_controller/test_process.rb:464:in `send!'
/Library/Ruby/Gems/1.8/gems/actionpack-2.1.0/lib/action_controller/test_process.rb:464:in `method_missing'
This line in url_for is the problem; specfically #url is nil.
#url.rewrite(rewrite_options(options))
And it seems that #url is initialized here:
def initialize_current_url
#url = UrlRewriter.new(request, params.clone)
end

This happens because url_for depends on stuff that's initialized during request processing. I assume your test looks something like this:
it "should do whatever when referrer is group thing" do
#request.env["HTTP_REFERER"] = url_for(#mock_group)
get :some_action
"something".should == "something"
end
url_for fails because it happens before the get. The easiest way to resolve the problem is to hard-code the URL in your test (i.e. change url\_for(#mock\_group) to "http://test.host/group/1"). The other option is to figure out how to get #controller to initialize #url before you call url_for. I think I've done that before, but I don't have the code around any more and it involved digging through action_controller's code.

Look at this. I think it is relevant.
http://jakescruggs.blogspot.com/2008/11/if-you-use-mocha-and-rspec-then-read.html

Related

Having trouble with Rspec expect(double).to receive(:message)

Having been inspired by Sandi Metz's approach to writing tests (http://www.confreaks.com/videos/2452-railsconf2013-the-magic-tricks-of-testing), I am trying to refactor a test for a Rails controller to assert that it is sending a command message properly.
Here are the relevant parts of the Application:
class DealsController < ApplicationController
def index
if params[:reset]
deal_filter.reset
...
class ApplicationController
def deal_filter
...
#deal_filter ||= DealFilter.new(args)
end
...
class DealFilter
def reset
...do work...
end
...
And here is the rspec test:
describe DealsController do
it "should send 'reset' to the deal_filter" do
df = instance_double("DealFilter")
get :index, reset: "true"
expect(df).to receive(:reset)
end
end
The test results that keep coming back are:
1) DealsController GET index for any user params contain 'reset' should send 'reset' to the deal_filter
Failure/Error: expect(df).to receive(:reset)
(Double "DealFilter (instance)").reset(any args)
expected: 1 time with any arguments
received: 0 times with any arguments
I have already confirmed that the reset param is being sent through the test and that the controller is following the appropriate path, yet the test continues to fail.
Can anyone suggest a possible reason for the failure or resources for further study? I am relatively new to object oriented thinking and using mocks with Rspec. Could it be that I have misunderstood the role of doubles?
Thanks for your time!
You need to make sure your double gets used. I think the best way to do that here is to stub the deal_filter method to return the double.
I addition I would isolate the expection, so that it's the only thing in the it block. This will make it easier to add more expections without duplication the setup logic.
describe DealsController do
let(:df) { instance_double("DealFilter") }
before do
allow(controller).to receive(:deal_filter).and_return(df)
get :index, reset: "true"
end
it "should send 'reset' to the deal_filter" do
expect(df).to have_received(:reset)
end
end
I think you're expecting your instance_double to be used automatically somewhere within the index action. That's not how doubles work. You can create a double and use it for things, but your code in the controller doesn't (and shouldn't) know anything about that double and so won't ever call anything on it.
For an example of how an instance double can actually be used see this documentation.
Another issue with your expectation is that you're not setting it early enough. When you expect an object to receive a method call there needs to be something that happens after that which would invoke that method. In your example the expectation to receive :reset is the very last line of your example.
I'd recommend reading up on how other people have tested controllers with rspec as a good starting place.

Rails: test a helper that needs access to the Rails environment (e.g. request.fullpath)

I have a helper that accesses request.fullpath. Within an isolated helper test, request is not available. What should I do? Can I somehow mock it or something like that?
I'm using the newest versions of Rails and RSpec. Here's what my helper looks like:
def item(*args, &block)
# some code
if request.fullpath == 'some-path'
# do some stuff
end
end
So the problematic code line is #4 where the helper needs access to the request object which isn't available in the helper spec.
Thanks a lot for help.
Yes, you can mock the request. I had a whole long answer here describing how to do that, but in fact that's not necessarily what you want.
Just call your helper method on the helper object in your example. Like so:
describe "#item" do
it "does whatever" do
helper.item.should ...
end
end
That will give you access to a test request object. If you need to specify a specific value for the request path, you can do so like this:
before :each do
helper.request.path = 'some-path'
end
Actually, for completeness, let me include my original answer, since depending on what you're trying to do it might still be helpful.
Here's how you can mock the request:
request = mock('request')
controller.stub(:request).and_return request
You can add stub methods to the returned request similarly
request.stub(:method).and_return return_value
And alternative syntax to mock & stub all in one line:
request = mock('request', :method => return_value)
Rspec will complain if your mock receives messages that you didn't stub. If there's other stuff Just call your request helper method on the helper object is doing that you don't care about in your test, you can shut rspec up by making the mock a "null object",example. like Like so
request = mock('request').as_null_object
It looks like all you probably need to get your specific test passing is this:
describe "#item" do
let(:request){ mock('request', :fullpath => 'some-path') }
before :each do
controller.stub(:request).and_return request
end
it "does whatever"
end
In a helper spec, you can access the request using controller.request (so controller.request.stub(:fullpath) { "whatever" } should work)

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.

RSpec mock_model and inherited_resources

I'm trying to write spec for inherited_resources controller. I decided to mock all integration with the database using rspec's mock_model. Unfortunately I can't write spec for create and update action because I'm getting the following error: https://gist.github.com/936947
Can someone help me with this issue?
I was having the same issue using flexmock.
the cause is that it does not use the update_attributes method to make the routing decision. It checks the resource.errors to see whether it is empty.
So to get it to respond properly, we will need to mock out the errors method as well.
Here is the pertinent code #line 248 in lib/inherited_resources/base_helpers.rb
def respond_with_dual_blocks(object, options, &block) #:nodoc:
args = (with_chain(object) << options)
case block.try(:arity)
when 2
respond_with(*args) do |responder|
blank_slate = InheritedResources::BlankSlate.new
if object.errors.empty?
block.call(responder, blank_slate)
else
block.call(blank_slate, responder)
end
end
when 1
respond_with(*args, &block)
else
options[:location] = block.call if block
respond_with(*args)
end
end
The failure messages are about the inability to access named routes from inside the controller, so I'm not sure that this has anything to do with mock_model. Have you tried the same examples using real models?

Rails test unit - helper with content_for

module ApplicationHelper
def title(page_title, show_title = true)
content_for(:title) do
page_title.to_s
end
#show_title = show_title
end
end
Anyone knows how can I test this helper using test unit?
For any helper testing in rails, you always start in tests/unit/helpers.
Since this is a ApplicationHelper, use the file called application_helper_test.rb
In that file you can have something like
test "displays page title" do
assert_equal "April 2010", title("April 2010", false)
end
You can test whatever is returned in a helper by just calling the method as usual, and asserting something is sent back.
Not knowing what you are doing, personally, there is too much going on in this method, but that could just be me.
I'd break these two out, so that your helper is just returning a page_title and another one is returning a "show_title" whatever that is. or is that like your switch to say " I should show this title on a page"?

Resources