2

I'm developing an internal API using Rails and I'm thinking to use ActiceResource to access it. I want to make integration test cases (ie. making http requests to the controllers in test environment) using ActiveResource but can't figure out how to set up the test cases.

In normal integration test cases you send requests to your own app with get/post methods. Somehow I should tell ActiceResource to connect to my app instead of making a real http connection.

All ActiveResource test examples I see are using mocks. I would like to avoid this and run my tests against my controller code instead. I'm using Rails 5 and Minitest.

Below is the test code which obviously does not work. If I try to run it, it will give an error:

require 'test_helper'

module MyApp
  class User < ActiveResource::Base
    self.site = 'http://www.example.com'
  end
end

class UserFlowsTest < ActionDispatch::IntegrationTest
  test "should get index" do
    users = MyApp::User.all

    assert_response :success
  end
end


▶ rails test test/integration/user_flows_test.rb
NoMethodError: undefined method `response_code' for nil:NilClass
    actionpack (5.2.1) lib/action_dispatch/testing/assertions/response.rb:81:in `generate_response_message'
    actionpack (5.2.1) lib/action_dispatch/testing/assertions/response.rb:31:in `assert_response'
    test/integration/user_flows_test.rb:13:in `block in <class:UserFlowsTest>'
talakoski
  • 131
  • 1
  • 8

1 Answers1

1

I found a way to do this. It is a hack but seems to work nicely.

In IntegrationTest Rails defines get/post/etc helpers methods which you then use in test cases to connect your Rails app. To make ActiveResource to use the helpers methods instead of making a real HTTP request, I monkey patched AR request method. I used global variable to send the current test case and to access the helper methods.

# Include this in test_helper.rb into ActiveSupport::TestCase class
module ActiveResourceMonkeyPatching
  module ::ActiveResource
    # We need to monkey patch AR:Connection to use IntegrtionTest helper
    # methods (get, post, ...) instead of Net:Http
    class Connection
      private

      # Makes a request to the remote service.
      def request(method, path, *arguments)
        result = ActiveSupport::Notifications
                .instrument('request.active_resource') do |payload|
          payload[:method] = method
          payload[:request_uri] =
            "#{site.scheme}://#{site.host}:#{site.port}#{path}"
          payload[:result] =
            $test.send(method,
                      path,
                      { params: arguments.first, headers: arguments.last }) &&
            $test.response
        end
        handle_response(result)
      rescue Timeout::Error => e
        raise TimeoutError.new(e.message)
      rescue OpenSSL::SSL::SSLError => e
        raise SSLError.new(e.message)
      end
    end

    # Lets also predefine site so we don't need to configure those in test cases
    class Base
      self.site = 'http://www.example.com'
    end
  end

  # We also monkey patch IntegrationTest to set the '$test' global variance which is
  # needed in ActiveResource
  class ActionDispatch::IntegrationTest
    def setup
      $test = self
      super
    end
  end
end

Since I started monkey patching I also added some more to pass global variable and setting the dummy site attribute. The actual test case is simple, see below. Since we are running this as a normal IntergrationTest, all the fixures in User model are loaded as usual.

require 'test_helper'

module MyApp
  class User < ActiveResource::Base
  end
end

class UserFlowsTest < ActionDispatch::IntegrationTest
  test "should get index" do
    users = MyApp::User.all

    assert_response :success
  end
end

This is a hack :) Using monkey patching means that above solution works with the current versions of Rails and ActiveResource and will fail if and when those are updated. If someone finds an other way to solve this I would be interested to know.

talakoski
  • 131
  • 1
  • 8