5

I have a private method in a controller

private 
  def body_builder
    review_queue = ReviewQueueApplication.where(id: params[:review_queue_id]).first
    ...
    ...
  end

I would like to test just the body_builder method, it is a method buidling the payload for an rest client api call. It needs access to the params however.

describe ReviewQueueApplicationsController, type: :controller do
  describe "when calling the post_review action" do
    it "should have the correct payload setup" do
      @review_queue_application = ReviewQueueApplication.create!(application_id: 1)
      params = ActionController::Parameters.new({ review_queue_id: @review_queue_application.id })
      expect(controller.send(:body_builder)).to eq(nil)
    end
  end
end

If I run the above it will send the body_builder method but then it will break because the params have not been set up correctly as they would be in a call to the action.

I could always create a conditional parameter for the body_builder method so that it either takes an argument or it will use the params like this def body_builder(review_queue_id = params[:review_queue_id]) and then in the test controller.send(:body_builder, params), but I feel that changing the code to make the test pass is wrong it should just test it as it is.

How can I get params into the controller before I send the private method to it?

8
  • My suggestion would be to actually CALL the RESTful entry point with the correct params. If you've got behavior that has to occur before your body_builder method can be called, you can mock and stub to channel the execution down the right path. Then you place an expectation on the controller.body_builder method rather than just calling it. I realize this is heavier than just calling the private method, but I've always felt that if one calls private private methods directly in tests you're kind of stepping outside the test "sandbox" for that object. Commented May 5, 2016 at 13:33
  • @jaydel, I totally get where you are coming from, the only issue is in testing the actual call to the entry point is that it will fire off the RestClient::Request action which is already tested, I want to assume that that portion works correctly but the building of the payload is neglected, if we add things to the payload that it is not expecting it should fail. Commented May 5, 2016 at 13:48
  • 1
    Yes, I understand your concern. I'm unfamiliar with the details of that particular behavior, but is it possible to mock and stub that part to build the payload in the ways you want to test. I've wandered outside the context I understand though, and you know it much better than I do, of course. so just food for thought Commented May 5, 2016 at 13:56
  • This is a great question you've asked. I have lived through a lot of contentious conversations about this issue. For whatever reason there seems to be some 'religion' around this topic. I tend to avoid taking those positions blindly and am still unresolved in many ways, so I'll be following this question :) Commented May 5, 2016 at 13:57
  • Yeah, I feel the same way, we break up the code in the controllers all the time like this and it make's sense to be able to test each method according to its responsibility. But having a global state like parameters make it tough to test them on their own. You have to constantly be aware of that rails black magic in the background. Commented May 5, 2016 at 14:02

1 Answer 1

3

I think you should be able to replace

params = ActionController::Parameters.new({ review_queue_id: @review_queue_application.id })

with

controller.params = ActionController::Parameters.new({ review_queue_id: @review_queue_application.id })

and you should be good. The params is just an attribute of the controller (the actual attribute is @_params but there are methods for accessing that ivar. Try putting controller.inspect in a view).

Sign up to request clarification or add additional context in comments.

6 Comments

by the way, having said this, you shouldn't be testing private methods, of course ;-)
shouldn't be testing them directly, perhaps? If they're complicated and have logic that can go wrong, they should be tested in my opinion.
I have a little policy that is if I get an exception: I immediately start the process by writing a test. This way the I know I will never get an exception, most of my private methods are carrying heavy IP requirements and break quite a bit. Even though it seems dirty by sending these methods off, my thinking is if it's really hard to test private methods then something is wrong with the class I am writing.
I believe the general opinion on this is that private methods should only be tested through public methods. So if your private method is causing exceptions, I would normally test that through the public method that uses the private method.
yes, that's what I mean by directly. You can test them specifically through isolation with mocking and stubbing and use public interfaces to drive those tests. my assertion (and it's just my opinion) is that a) they should be tested and b) your tests should not use .send to test them.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.