How do you test a Rails controller method exposed as a helper_method?

后端 未结 5 2476
眼角桃花
眼角桃花 2021-02-18 17:22

They don\'t seem to be accessible from ActionView::TestCase

相关标签:
5条回答
  • 2021-02-18 17:49

    That's right, helper methods are not exposed in the view tests - but they can be tested in your functional tests. And since they are defined in the controller, this is the right place to test them. Your helper method is probably defined as private, so you'll have to use Ruby metaprogramming to call the method.

    app/controllers/posts_controller.rb:

    class PostsController < ApplicationController
    
      private
    
      def format_something
        "abc"
      end
      helper_method :format_something
    end
    

    test/functional/posts_controller_test.rb:

    require 'test_helper'
    
    class PostsControllerTest < ActionController::TestCase
      test "the format_something helper returns 'abc'" do
        assert_equal 'abc', @controller.send(:format_something)
      end
    end
    
    0 讨论(0)
  • 2021-02-18 17:50

    Indeed they're not. The view tests are specifically for the views. They don't load the controllers.
    You should mock this method and make it return whatever is appropriate depending of your context.

    0 讨论(0)
  • I've struggled with this for a bit, because the accepted answer didn't actually test whether the method was exposed as a helper method.

    That said, we can use the #helpers method to get a proxy for testing.

    For example:

    class FooController < ApplicationController
      private
    
      def bar
        'bar'
      end
    
      helper_method :bar
    end
    

    Can be tested with:

    require 'test_helper'
    
    class FooControllerTest < ActionController::TestCase
      test 'bar is a helper method' do
        assert_equal 'bar', @controller.helpers.bar
      end
    end
    
    0 讨论(0)
  • 2021-02-18 17:59

    You could test @controller.view_context from your functional/controller tests. This method is available in Rails 3, 4, and 5, as far as I can tell.

    app/controllers/application_controller.rb

    class ApplicationController < ActionController::Base
      helper_method :current_user
      # ...
    end
    

    test/controllers/application_controller_test.rb

    require 'test_helper'
    
    class ApplicationControllerTest < ActionController::TestCase
      test 'current_user helper exists in view context' do
        assert_respond_to @controller.view_context, :current_user
      end
    end
    

    If you didn't want to test one of your controller subclasses, you could also create a test controller to verify that the method in the view_context is the same one from the controller and not from one of your view helpers.

    class ApplicationControllerHelperTest < ActionController::TestCase
      class TestController < ApplicationController
        private
        def current_user
          User.new
        end
      end
    
      tests TestController
    
      test 'current_user helper exists in view context' do
        assert_respond_to @controller.view_context, :current_user
      end
    
      test 'current_user returns value from controller' do
        assert_instance_of User, @controller.view_context.current_user
      end
    end
    

    Or, more likely, you'd want to be able to test the helper in the presence of a request.

    class ApplicationControllerHelperTest < ActionController::TestCase
      class TestController < ApplicationController
        def index
          render plain: 'Hello, World!'
        end
      end
    
      tests TestController
    
      def with_routing
        # http://api.rubyonrails.org/classes/ActionDispatch/Assertions/RoutingAssertions.html#method-i-with_routing
        # http://guides.rubyonrails.org/routing.html#connecting-urls-to-code
        super do |set|
          set.draw do
            get 'application_controller_test/test', to: 'application_controller_test/test#index'
          end
    
          yield
        end
      end
    
      test 'current_user helper exists in view context' do
        assert_respond_to @controller.view_context, :current_user
      end
    
      test 'current_user returns value from controller' do
        with_routing do
          # set up your session, perhaps
          user = User.create! username: 'testuser'
          session[:user_id] = user.id
    
          get :index
          assert_equal user.id, @controller.view_context.current_user.id
        end
      end
    end
    
    0 讨论(0)
  • 2021-02-18 18:03

    This feels awkward, because you're getting around encapsulation by using send on a private method.

    A better approach is to put the helper method in a module in the /controller/concerns directory, and create tests specifically just for this module.

    e.g. in app controller/posts_controller.rb

    class PostsController < ApplicationController
      include Formattable
    end
    

    in app/controller/concerns/formattable.rb

      module Concerns
        module Formattable
          extend ActiveSupport::Concern # adds the new hot concerns stuff, optional
    
          def format_something
            "abc"
          end
        end
      end
    

    And in the test/functional/concerns/formattable_test.rb

    require 'test_helper'
    
    # setup a fake controller to test against
    class FormattableTestController
      include Concerns::Formattable
    end
    
    class FormattableTest < ActiveSupport::TestCase
    
     test "the format_something helper returns 'abc'" do
        controller = FormattableTestController.new
        assert_equal 'abc', controller.format_something
      end
    
    end
    
    0 讨论(0)
提交回复
热议问题