New movie rental service

I’ve spent a fair chunk of time this morning trying to get my functional tests on my rails app working with the login component. Here’s the result of my time, presented in the hopes that it will help someone else who’s stuck on the same thing.

I’m using ModelSecurity for the user login component because I’d also like to use the model security bits. Since I’ve got a before_filter to verify a user is logged in in my controller, I need to log in the user before I can test anything. Logging in the user is easy, right - I just post to the login action of the user controller. Here it is, wrapped in a function because I’ll have to call it multiple times:


  def login_Neo
    post :login, :user=>{:login=>users(:neo).login, :password=>NEO_PASSWORD}
  end

  def test_list
    login_Neo
    get :list
    assert_response :success
    assert_template 'list'
    assert_not_nil assigns(:recipes)
  end

Ok, I’m sure that I’ve got a problem because my controller doesn’t have a login action, but I decided to run it and see what happens.

Expected response to be a <:success>, but was <302>

Yep, I’m being redirected to the user controller because of my before_filter.

Now, I know that I need to make it post to the user controller, not the recipedex controller. Much googling, reading and thinking later, I discover that the post method being used here is defined in ActionPack from Rails. And, once I dig up the source for that, I discovered that post simply uses the @controller instance variable.

Aha! One simple change and I’m on my way:


  def login_Neo
    controller_bak = @controller
    @controller = UserController.new
    post :login, :user=>{:login=>users(:neo).login, :password=>NEO_PASSWORD}
    @controller = controller_bak
  end

Now, it posts where I want it to, sets User.current as I expect and everything works!

7 Responses to “Testing Rails - Posting to another controller”

  1. Michael Hartl says:

    I was stuck on just this thing. Thanks for posting the solution!

    Michael

  2. matt says:

    I’m glad I was able to help.

    -Matt

  3. Anders says:

    Thanks for a simple solution to my insolvable problem. I have one question though. Where do you put your function login_Neo? Is it possible to have accessible from all test-files?

    Anders

  4. matt says:

    Hi Anders - You can put the login_Neo function in your test_helper.rb file. That way it’ll be available in all of your test scripts. For what it’s worth, I’ve moved away from ModelSecurity and am now using acts_as_authenticated. I had too many problems getting the testing to work correctly and it didn’t seem like ModelSecurity was being developed at all. If I need it, I can go back to ModelSecurity for actually securing my models, but I found the user authentication/authorization more usable in acts_as_authenticated.

    -Matt

  5. Brittain says:

    Anders thanks for solving the post problem, a little compensation for ya, here’s the helper I created with it:

    # Swap the controller for the length of the block, then switch back
    def swap_controllers(new_controller, &block)
    old_c = @controller
    @controller = new_controller
    yield
    @controller = old_c
    end

    Allows for:
    # From within ConvsController

    # Destroy a conv from PagesController then verify returns to Convs …
    swap_controllers(PagesController.new) {
    get :service
    swap_controllers(ConvsController.new) {
    check_try_destroy(20) {|id| is_destroyed(id)}
    assert_redirected_to :controller => “pages”, :action => “service”
    }
    }

  6. matt says:

    Thanks for that, Brittain. Looks like a nice little bundling of the functionality.

    I haven’t worked with it yet, but Integration Testing (available with Rails 1.1) should take care of this issue for us in a nice clean way.

    -Matt

  7. Ben Kittrell says:

    Awesome Matt. This is just what I was looking for.

    Thanks