Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

can not stub/mock helper methods #396

Closed
unorthodoxgeek opened this issue Apr 29, 2013 · 14 comments
Closed

can not stub/mock helper methods #396

unorthodoxgeek opened this issue Apr 29, 2013 · 14 comments

Comments

@unorthodoxgeek
Copy link

because of the odd and extremely complex way helpers are defined in grape, there is no way I can find to mock/stub helper methods. This is a very serious flaw in this framework IMHO, as testing it proves very difficult.
for instance, I have a helper method named current_user, it returns the obvious value.

I want to test an api request, once when logged in, once when logged out. There's absolutely no way I could find which allows me to do something.stub(:current_user).and_return(user)

This makes it VERY difficult to test grape, on the verge of actually making it impossible.

If the helper was more sensibly made, as a simple module applied on the request class, there would be an easy way to stub it thus test everything which is needed for the development using this library.

@mbleigh
Copy link
Contributor

mbleigh commented Apr 29, 2013

Can you elaborate on what the more "sensibly made" module pattern would look like in practice? Grape's helpers are essentially already just on-the-fly Modules defined to run at a specific context.

I'm certainly open to a better way to provide stubbing/mocking of helpers, but I'm not clear on your suggestion.

@unorthodoxgeek
Copy link
Author

if you look at the way rails does it, it uses a module to hold the helper methods, and applies that module on an object which is defined on the class level of the controller. thus FooController.instance.helper will be an instance of some class, with that helper included into it. (I'm simplifying this a bit)
then, the actual helpers are called on that object (with the request context), using method_missing or a similar way of doing it.
This way, the helper object is accessible for opening up and fiddling with, including of course mocking and stubbing it.

@dblock
Copy link
Member

dblock commented Apr 29, 2013

@unorthodoxgeek where's that code in Rails? We do define helpers on a module and can make helpers keep that module around:

    def helpers
      @helpers ||= begin
        m = Module.new
        settings.stack.each{ |frame| m.send :include, frame[:helpers] if frame[:helpers] }
        m
      end
    end

But then Grape does extend helpers inside run, which loses any stubs.

Any suggestion of how to reimplement this welcome, right now I am thinking @mbleigh's #397 might be too hacky.

@unorthodoxgeek
Copy link
Author

where's the extend helpers line? I didn't find it...

On Tue, Apr 30, 2013 at 2:57 AM, Daniel Doubrovkine (dB.) <
notifications@github.com> wrote:

@unorthodoxgeek https://github.com/unorthodoxgeek where's that code in
Rails? We do define helpers on a module and can make helpers keep that
module around:

def helpers
  @helpers ||= begin
    m = Module.new
    settings.stack.each{ |frame| m.send :include, frame[:helpers] if frame[:helpers] }
    m
  end
end

But then Grape does extend helpers inside runhttps://github.com/intridea/grape/blob/master/lib/grape/endpoint.rb#L366,
which loses any stubs.

Any suggestion of how to reimplement this welcome, right now I am thinking
@mbleigh https://github.com/mbleigh's #397https://github.com/intridea/grape/issues/397might be too hacky.


Reply to this email directly or view it on GitHubhttps://github.com//issues/396#issuecomment-17202224
.

@dblock
Copy link
Member

dblock commented Apr 30, 2013

@idyll
Copy link
Contributor

idyll commented May 3, 2013

Not sure this is the best way to do things, but here's how I have been testing my helpers.

I inject the helpers into a mock API object but using Rack::Test to handle the request and response methods.

require "spec_helper"
require "rack/test"

# app method is needed for rack-test
def app
  MyRailsApp::Application
end

# we create a mock API object that uses Rack::Test to stub out a mock request and response.
def api
  api_double = double("api")
  api_double.stub(:request) do  
    last_request
  end
  api_double.stub(:response) do 
    last_response
  end
  api_double.stub(:env) do 
    last_request.env
  end
  api_double.extend Helpers
end

describe "API Helper Methods" do
  include Rack::Test::Methods
  fixtures :users

  before(:all) do
    @bad_request = {:code => 400, :message => "Bad request"}
    @good_request = {:code => 200}
  end

  describe "Helper method tests that need a valid auth token" do
    before(:each) do  
      @api = api
      @token_auth =  token_based_auth( users(:user).authentication_token )
      get "/api/v1/users", nil, {'HTTP_AUTHORIZATION' =>  @token_auth }
    end

    it "should authenticate or create a new token" do
      @api.should_not_receive(:error!).with("Access Denied", 401)
      @api.authenticate_user!
    end
  end

  describe "Helper methods that don't require a user" do
    before(:each) do
      @api = api  
    end


    it "should call error from not modified" do
      @api.should_receive(:error!).with("Not Modified", 304)
      @api.not_modified!
    end

    it "should call error from access denied" do
      @api.should_receive(:error!).with("Access Denied", 401)
      @api.access_denied!
    end

    it "should call error from bad request" do
      @api.should_receive(:error!).with("Bad Request", 400)
      @api.bad_request!
    end

    it "should call error from not found" do
      @api.should_receive(:error!).with("Not Found", 404)
      @api.not_found!
    end

    it "should call error from forbidden" do
      @api.should_receive(:error!).with("Forbidden", 403)
      @api.forbidden_request!
    end

    it "should call error from invalid request" do
      @api.should_receive(:error!).with("Message", 422)
      @api.invalid_request!("Message")
    end

  end

end

For the most part the helpers are calling other code. To test then you just want to make sure the code is called correctly. The code called should be tested elsewhere.

@bountysource-support
Copy link

Hi all, there is a $100 bounty on this issue at Bountysource. The bounty will go to the dev whose PR closes this.

@kiela
Copy link
Contributor

kiela commented Mar 4, 2014

If you use RSpec 3.x you can stub helpers in this way:

allow_any_instance_of(Api::V1::Helpers).to receive(:method_name)

@dblock
Copy link
Member

dblock commented Mar 5, 2014

@kiela Have you tried it? In your example Api::V1::Helpers is a module, no?

@kiela
Copy link
Contributor

kiela commented Mar 5, 2014

@dblock

  • yes, I have tried it and it works for me
  • yes, Api::V1::Helpers is a module

I will prepare sample application in near future

@dblock
Copy link
Member

dblock commented Mar 5, 2014

@kiela Tests and a README update would be great for a pull request to close this!

@kiela
Copy link
Contributor

kiela commented Mar 7, 2014

@dblock I will take a look again on that during weekend and make a PR if this is approach valid

@WattsInABox
Copy link

That method of stubbing does not work in mocha. any_instance called on a module raises a NoMethodError as you would expect.

👍

@dblock
Copy link
Member

dblock commented Apr 12, 2014

After much discussion around this we went with #397. Thx @mbleigh.

@dblock dblock closed this as completed Apr 12, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants