ruby-on-railsrubyruby-on-rails-5integration-testingactiondispatch

ActionDispatch::IntegrationTest suppresses exceptions


When debugging failing integration tests, I keep running into the same problem where the exceptions raised in my code are suppressed and not shown in the testing output.

For example, for the following controller and test:

class RegistrationController::ApplicationController
  def create
    # some code that raises an exception
  end
end
class RegistrationFlowTest < ActionDispatch::IntegrationTest

  test 'user registers successfully' do
    post sign_up_path, params: { username: 'arnold', password: '123' }
    assert_response :success  
  end

end

The output is something like

Minitest::Assertion: Expected response to be a <2XX: success>, but was a <500: Internal Server Error>

Is there a way to see the exact raised exception? Instead of just the difference of HTTP response code?

Thanks!

Simon


Solution

  • My recommended approach to this issue would be to actually parse the response provided by Rails (at least by default in test and development environments) which includes the stacktrace for the error and handle that in the case that your test fails. This has the advantage that it won't output the stacktrace when errors are raised that don't result in failing tests (e.g. scenarios where you are intentionally testing how failures are handled).

    This little module that I've crafted will allow you to call assert_response_with_errors to assert the response to a call but output the exception message and stack trace in a readable format when the response is not what you expected.

    module ActionDispatch
      module Assertions
        module CustomResponseAssertions
          # Use this method when you want to assert a response body but also print the exception
          # and full stack trace to the test console.
          # Useful when you are getting errors in integration tests but don't know what they are.
          #
          # Example:
          # user_session.post create_gene_path, params: {...}
          # user_session.assert_response_with_errors :created
          def assert_response_with_errors(type, message = nil)
            assert_response(type, message)
          rescue Minitest::Assertion => e
            message = e.message
            message += "\nException message: #{@response.parsed_body[:exception]}"
            stack_trace = @response.parsed_body[:traces][:'Application Trace'].map { |line| line[:trace] }.join "\n"
            message += "\nException stack trace start"
            message += "\n#{stack_trace}"
            message += "\nException stack trace end"
            raise Minitest::Assertion, message
          end
        end
      end
    end
    

    To use this, you need to include it into ActionDispatch::Assertions before Rails has loaded its stack in your test_helper.rb. So just prepend the include into your test_helper.rb, like this:

    ActionDispatch::Assertions.include ActionDispatch::Assertions::CustomResponseAssertions
    require File.expand_path('../../config/environment', __FILE__)
    require 'rails/test_help'
    ...