My ApplicationController exposes a method (e.g. sort_direction) to the view templates by using helper_method :sort_direction. I then use this method in another method (e.g. sort_link) of a view helper (application_helper.rb).
When testing the sort_link method with RSpec (in application_helper_spec.rb) I have to stub sort_direction as the test seems to run complete independent from the controllers (and thereby by its to the view templates exposed methods).
Unfortunately I could not find out how to stub that sort_direction method of the controller. I always get "undefined method".
Here is what I tried so far (inside application_helper_spec.rb):
helper.stub(:sort_direction)
controller.stub(:sort_direction)
view.stub(:sort_direction)
self.stub(:sort_direction)
Any suggestions how I can stub that method?
Here the error I get:
NoMethodError:
undefined method `sort_direction' for #<RSpec::Core::ExampleGroup::Nested_1::Nested_1:0xb641434c>
David Chelimsky solved that problem here: http://groups.google.com/group/rspec/browse_thread/thread/cc44ca12c6816053
Simply call in the spec all methods on the helper object:
it "should work" do
helper.stub(:sort_direction)
helper.sort_link(...).should == ...
end
Related
I've got this helper which I'm trying to write tests for in Minitest. The helper calls another method depending on the object class I'm passing as an argument, like so:
def label_for(object)
status = object&.status
case object.class.name
when "Subscription"
class_for_subscription_status(status)
when "Payment"
class_for_payment_status(status)
when "Purchase"
class_for_purchase_status(status)
when "Invoice"
class_for_invoice_status(status)
when "Ticket"
class_for_ticket_status(status)
end
Each individual method is already tested somewhere else, so I just need to test that if I pass a class Subscription object to label_for, it will invoke class_for_subscription_status(status) and not something else.
This is the test I've come up with, but I get NoMethodError: undefined method ``class_for_subscription_status' for #<AuxiliariesHelperTest errors.
test "#label_for(object) should invoke the right helper if object is of class Subscription" do
AuxiliariesHelperTest.any_instance.stubs(:label_for).with(subscriptions(:user)).returns(:class_for_subscription_status)
assert_equal class_for_subscription_status(subscriptions(:user).status), label_for(subscriptions(:user))
end
What am I doing wrong?
Could you add the whole classes? Is a little bit hard to guess with just this snippet.
One of the problems I see is that you are stubbing a method from the AuxiliariesHelperTest class, instead of the AuxiliariesHelper class.
Another possible issue is that your helper seems to be a module and not a class, and you should include the helper in your test file. Or your test class should inherit from ActionView::TestCase. Something like this might help:
class AuxiliariesHelperTest < ActionView::TestCase
include AuxiliariesHelper
test "#label_for(object) should invoke the right helper if object is of class Subscription" do
AuxiliariesHelper.any_instance.stubs(:label_for).with(subscriptions(:user)).returns(:class_for_subscription_status)
assert_equal class_for_subscription_status(subscriptions(:user).status), label_for(subscriptions(:user))
end
end
Although in my opinion, you should not stub the method, but expect that the correct method is called:
class AuxiliariesHelperTest < ActionView::TestCase
include AuxiliariesHelper
test "#label_for(object) should invoke the right helper if object is of class Subscription" do
AuxiliariesHelper.any_instance.expects(:label_for).with(subscriptions(:user).status)
label_for(subscriptions(:user))
end
end
Using Rails 3.2
I'm trying to call expire_fragment, the Rails view method, from a helper but getting an error:
undefined method `expire_fragment' for #<#<Class:0x00000118977110>:0x00000103b853b8>
I'm trying to conditionally clear the cache. This is the helper method call in my view
clear_cache_keys_if(params[:cc], [#product, :search_filters])
And in the helper
def clear_cache_keys_if(condition, keys = [])
if condition
keys.each do |key|
expire_fragment(key)
end
end
end
I would have thought that the Rails fragment caching methods would be accessible in a helper module, but that doesn't seem to be the case.
I changed it to
controller.expire_fragment(key)
And that worked.
This method is available in the view. It's available from the controller. I don't really understand why it is not available in a helper. What am I missing here? Why isn't it available in the helper and what is the best way to expose it? Thanks
I am writing controller tests for an application that I did not build, so it's definitely been a learning process. This is my first time encountering a controller that inherits directly from AbstractController::Base. It does not behave, obviously, the same as other controllers.
Its format is roughly:
class SchwadGenericController < AbstractController::Base
def schwad_method var_one, var_two = nil, var_three = nil
if var_two.blank?
var_one.generic_method
end
render template: "schwad_templates/generic_template", layout: false
end
end
I tried normal testing, this is where I am currently at to get ANYTHING to happen.
require 'rails_helper'
describe SchwadGenericController do
# before(:each) do
# SchwadGenericController.skip_authorize_resource
# end
# login_user
let!(:variable){ create(:my_factory_variable) }
describe 'controller methods' do
it 'should hit this method' do
binding.pry
SchwadGenericController.schwad_method(variable)
# expect(response).to_render template: "schwad_templates/generic_template"
end
end
end
And here is roughly where my failures are landing.
Failures:
1) SchwadGenericController controller methods should hit this method
Failure/Error: Unable to find matching line from backtrace
NoMethodError:
undefined method `request=' for # <SchwadGenericController:0x007f8022db0a20>
I read up on abstract controllers and their role in rails here: https://www.mobomo.com/2012/06/and-you-thought-render-farms-were-just-for-pixar/
I read up on the docs here: http://api.rubyonrails.org/classes/AbstractController/Base.html
I would really appreciate another set of eyes on this and guidance as to how you guys have tested controllers and their methods, with controllers that are inheriting from AbstractController::Base.... What am I missing?
-Schwad
After some testing, I don't think this is possible. Controller specs are just wrappers for Rails functional tests which test classes inheriting from ActionController::Base. For controller tests to even run, the controller must support the request and response objects, which is not the case of AbstractController::Base (these are defined in ActionController::Base). That is why you get the particular error when you run the test. For the same reason, you will not be able to use the controller spec helpers (expects) such as to_render because, again, they are defined only for controller specs and your controller class is not a "controller" in the "controller specs" sense.
The only option you seem to have for testing is to test the controller just as any other plain ruby class. You'd need to move your test out of the spec/controllers directory to some other, e.g. spec/abstract_controllers and then you'd have to give up all controller spec helpers and test just calling the instance methods, e.g.:
describe 'controller methods' do
it 'should hit this method' do
c = SchwadGenericController.new
expect(c).to receive(:render).with(template: "schwad_templates/generic_template", layout: false)
c.schwad_method(variable)
end
end
Extending directly from AbstractController::Base seems the likely source of the error to me. Unless you're doing something very nonconventional there should be no reason to do this.
Are you sure you don't intend to inherit from ActionController::Base? There's a whole bunch of modules in ActionController required for rendering which is probably explains the error on a missing method in your tests.
If switching to ActionController::Base doesn't work. Try running app.get "/path/to/action" from the rails console. Do you get the same error?
In my rails project I have a presenter method that has a helper view method in it. This helper method is already tested so I would like to stub out it out in my test. How may I write this in my spec.
If you're trying to stub it in your view spec then how about something like this:
before(:each) do
view.stub!(:my_helper).and_return(something)
end
Here's my situation - I have a helper named LayoutHelper that I use to help me build my layout, and I'm trying to test the following method.
It has a method in LayoutHelper that looks similar to this:
def show_login_form?
return false if #hide_login_form || logged_in?
return true
end
I also have the following in my application.rb:
helper_method :logged_in?
def logged_in?
return #logged_in_user.present?
end
And finally, my test file looks like this:
require File.dirname(__FILE__) + '/../../test_helper'
class LayoutHelperTest < ActionView::TestCase
def test_hide_login_form
assert(show_login_form?)
hide_login_form
assert(!show_login_form?)
end
end
My problem now is that when I run this test, I get this:
NoMethodError: undefined method `logged_in?' for #<LayoutHelperTest:0xb7a7aca8>
app/helpers/layout_helper.rb:17:in `show_login_form?'
layout_helper_test.rb:12:in `test_hide_login_form'
I'm wondering if I'm doing this the correct Rails way, and if so, what I need to change in order to get this test to work.
I have to say, this stumped me at first - but I finally figured out what wasn't quite right.
Helpers are html generators, and should be built in a bubble - not knowing what's going on in sessions, controllers, etc. Your show_login_form? method isn't generating code, it's working with controller methods. It should be in application_controller.rb itself, and tested in controller tests.
Once you move it here, you should have no problem testing. Oh, and this might be a shorter, sweeter way to write the method itself:
def show_login_form?
!(#hide_login_form || logged_in?)
end
I hope this helps. It certainly got me to research helper tests, which I've neglected until now.