14

My helper code looks like this (and works fine btw):

module ProvidersHelper
  def call_to_review(provider)
    if user_signed_in? && review = Review.find_by_provider_id_and_user_id(provider.id, current_user.id)
      link_to "Edit Your Review", edit_provider_review_path(provider, review), :class => "call_to_review"
    else
      link_to "Review This Provider", new_provider_review_path(provider), :class => "call_to_review"
    end
  end
end

Unfortunately, this produces the following error when I run my tests:

 undefined method `user_signed_in?' for #<ActionView::Base:0x00000106314640>
 # ./app/helpers/providers_helper.rb:3:in `call_to_review'

Clearly the Devise::Controllers::Helpers are not being included in my helpers when rspec is running the test. Any suggestions that might help this work?

Edit: to provide a bit more information, my spec_helper does have this:

config.include Devise::TestHelpers, :type => :controller
config.include Devise::TestHelpers, :type => :view
config.include Devise::TestHelpers, :type => :helper

(Sadly, I couldn't get it to work with :type => [:controller, :view, :helper])

Anyway I believe that these lines add the sign_in(scope, object) (and other) test helpers to your tests. They don't add the helpers that you would actually leverage in your controller / view code.

Jonathan Tran
  • 15,214
  • 9
  • 60
  • 67
steve
  • 3,276
  • 27
  • 25
  • I'm having this problem too. I'm very interested in the answer. Things like 'current_user' don't exist when I run the test. It's probably the same issue that you are having. This is the one thing I don't like about dynamic languages - sometimes there's a lot of metaprogramming and 'magic' going on... and you have no idea what you need to call to get the desired result... so you end up wasting an hour of time looking for the method to call to make a test pass, thus defeating the productivity benefits of using the dynamic language in the first place :/ – egervari May 21 '11 at 02:50
  • Here is another example of this problem: NameError: undefined local variable or method `current_user' for # – egervari May 21 '11 at 02:56
  • I started a bounty for your question. I hope the person who answer also solves my problem at the same time. – egervari May 21 '11 at 04:35
  • 1
    Something you can do in the meantime is add "user_signed_in?" method into the test helper itself. Yes, you are duplicating functionality that is SUPPOSED to be provided, but it will work. – egervari May 21 '11 at 13:06

5 Answers5

12

I think the philosophy of rspec is to test the view/helpers/models in total isolation as much as possible. So in this case, i would stub out the user_signed_in? and returns false or true and my results should change appropriately. This gives you a clean isolated test.

nathanvda
  • 49,707
  • 13
  • 117
  • 139
  • This is pretty much the only answer I could come up with... but why does this work for controllers then? If Rspec wants us to stub these out... then why are they provided with controller tests but not helpers? Is this just a bug? :/ – egervari May 21 '11 at 21:22
  • Controllers could be used for integration testing, so that's why (I guess) a more complete loop is possible. So in the controller tests, i include the `Devise::TestHelpers` and there it just works. Inside your controllers specs, you could also render the views (which in general I do not do --most of the times I even tend to stub out all ActiveRecord access), so the views/helpers/... should work there. – nathanvda May 22 '11 at 13:46
  • 6
    How does one stub these out in a helper spec? The few variations I tired didn't work. Do the stubs go in a before block? Do you stub them off the helper object? – Chris Bloom Nov 29 '11 at 22:09
  • Could you ask this in a separate question? Then you can add the code you want to test, and how you tried to do that. A bit easier to discuss then. – nathanvda Nov 30 '11 at 09:28
  • When testing a helper-module, there is no controller. So if you depend on some methods defined in a controller, you have to stub them. Actually this makes good sense and makes the tests quicker. Also it also makes it very clear, in your test, on which methods you depend, and which "interface". HTH. – nathanvda Jan 03 '13 at 23:56
  • If you're using this for view specs in rspec, you'll want to stub it on `view`, not `helper`, like this `allow(view).to receive(:user_signed_in?).and_return(true)`. [Source in rspec docs][https://www.relishapp.com/rspec/rspec-rails/v/3-3/docs/view-specs/view-spec#passing-view-spec-that-stubs-a-helper-method] – NealJMD Feb 03 '16 at 16:19
1

Are you currently including the test Helpers as suggested in the wiki?

# spec_helper.rb:
RSpec.configure do |config|
  config.include Devise::TestHelpers, :type => :controller
end

type would be probably helper in your case.

polarblau
  • 17,649
  • 7
  • 63
  • 84
  • Yes, sorry I forgot to mention that. From my `spec_helper.rb`: `config.include Devise::TestHelpers, :type => :helper` – steve Jan 12 '11 at 01:40
  • I don't believe this will fix it. I am having the same problem. The problem is that references to 'current_user' and the like inside of the helper still don't exist when you run the test. – egervari May 21 '11 at 02:49
0

This has not been solved to my satisfaction and probably never will be. I think the best work-around for now is to manually stub helper.current_user and any other Devise methods you use in the helper method you're testing.

Yes, Devise provides these stubbing facilities for controller and view specs. I suspect that it's something about the combination of Devise/Rails/Test::Unit/Rspec that proves this to be difficult for helper specs.

steve
  • 3,276
  • 27
  • 25
0

my helper test uses Devise and cancan and works without stubbing anything (but I'm not sure if it is better to actually stub everything).

Here's the gist: https://gist.github.com/shotty01/5317463 i also tried to add user_signed_in? in the helper method and it still was fine.

The following is required:

add to spec_helper.rb:
config.include Devise::TestHelpers, :type => :helper

My spec gems:

rspec (2.10.0)
rspec-core (2.10.1)
rspec-expectations (2.10.0)
rspec-mocks (2.10.1)
rspec-rails (2.10.1)

of course you can sign in without factory girl, you just have to rewrite the ValidUserHelper methods to create a user directly or from fixtures.

Jon Lin
  • 142,182
  • 29
  • 220
  • 220
Shotty
  • 1
  • 1
0

Maybe try putting this is in a before block?

  @request.env["devise.mapping"] = :user
monocle
  • 5,866
  • 2
  • 26
  • 22
  • That has no effect for me :( Try making a helper test where a helper method just returns "current_user". If you can get this test to pass, I am sure it would solve my problem and his problem. I can't figure it out. – egervari May 21 '11 at 05:41
  • 3
    adding 'def current_user { @user } seems to solve my issue, but it seems kind of redundant to mock the methods that devise is supposed to provide on your behalf :/ – egervari May 21 '11 at 06:03