Test for HTTP status code in some RSpec rails request exampes, but for raised exception in others Test for HTTP status code in some RSpec rails request exampes, but for raised exception in others ruby ruby

Test for HTTP status code in some RSpec rails request exampes, but for raised exception in others


You'd want to use RSpec filters for that. If you do it this way, the modification to Rails.application.config.action_dispatch.show_exceptions will be local to the example and not interfere with your other tests:

# This configure block can be moved into a spec helperRSpec.configure do |config|  config.before(:example, exceptions: :catch) do    allow(Rails.application.config.action_dispatch).to receive(:show_exceptions) { true }  endendRSpec.describe 'POST' do  let(:perform_request) { post '/my/api/my_ressource', request_body }  context 'without mandatory attribute' do    let(:request_body) do      {}.to_json    end    it 'raises a ParameterMissing error' do      expect { perform_request }.to raise_error ActionController::ParameterMissing    end    context 'in production', exceptions: :catch do      it 'reports BAD REQUEST (HTTP status 400)' do        perform_request        expect(response).to be_a_bad_request      end    end  endend

The exceptions: :catch is "arbitrary metadata" in RSpec speak, I chose the naming here for readability.


Returning nil from a partially mocked application config with

    context 'in production' do      before do        allow(Rails.application.config.action_dispatch).to receive(:show_exceptions)      end      it 'reports BAD REQUEST (HTTP status 400)' do        perform_request        expect(response).to be_a_bad_request      end    end

or more explicitly with

    context 'in production' do      before do        allow(Rails.application.config.action_dispatch).to receive(:show_exceptions).and_return nil      end      it 'reports BAD REQUEST (HTTP status 400)' do        perform_request        expect(response).to be_a_bad_request      end    end

would work if that was the only example being run. But if it was, we could just as well drop the setting from config/environments/test.rb, so this is a bit moot. When there are several examples, this will not work, as Rails.application.env_config(), which queries this setting, caches its result.


Mocking Rails.application.env_config() to return a modified result

    context 'in production' do      before do        # We don't really want to test in a production environment,        # just in a slightly deviating test environment,        # so use the current test environment as a starting point ...        pseudo_production_config = Rails.application.env_config.clone        # ... and just remove the one test-specific setting we don't want here:        pseudo_production_config.delete 'action_dispatch.show_exceptions'        # Then let `Rails.application.env_config()` return that modified Hash        # for subsequent calls within this RSpec context.        allow(Rails.application).to receive(:env_config).                                        and_return pseudo_production_config      end      it 'reports BAD REQUEST (HTTP status 400)' do        perform_request        expect(response).to be_a_bad_request      end    end

will do the trick. Note that we clone the result from env_config(), lest we modify the original Hash which would affect all examples.