ruby-on-railsrspec-rails

Testing environment dependent routing with RSPEC


I have some routes that should only exist in the development environment but I can't get the specs for this working. Any ideas what I am doing wrong? I initially tried this in the controller spec but then realised I need :type => :routing to use be_routable so have separated this out. I have debugged and Rails.env has the values I expect in each context. Do I need to reload the routes? Have tried to do this but can't find a syntax that rspec is happy with...

Snippet from routes.rb:

    resources :users do
        collection do
          ...
          if Rails.env.development?
            get :new_development_account
            post :create_development_account
          end
        end
        ...

Routing spec:

require 'spec_helper'

describe "routes for users controller", :type => :routing do

    context "production environment" do
      it "development routes do not exist" do
        allow(Rails).to receive(:env) { "production".inquiry }
        expect(:get => "/ims/users/new_development_account").not_to be_routable
        expect(:post => "/ims/users/create_development_account").not_to be_routable
      end
    end

    context "development environment" do
      it "development routes exist" do
        allow(Rails).to receive(:env) { "development".inquiry }
        expect(:get => "/ims/users/new_development_account").to be_routable
        expect(:post => "/ims/users/create_development_account").to be_routable
      end
    end
end

What is particularly odd is that it fails both tests:

Failures:

  1) routes for users controller production environment development routes do not exist
     Failure/Error: expect(:get => "/ims/users/new_development_account").not_to be_routable
       expected {:get=>"/ims/users/new_development_account"} not to be routable, but it routes to {:action=>"show", :controller=>"ims/users", :id=>"new_development_account"}
     # /var/code/bundle/ruby/2.2.0/gems/given_core-3.5.4/lib/given/rspec/monkey.rb:31:in `handle_matcher'
     # ./spec/routing/users_controller_spec.rb:9:in `block (3 levels) in <top (required)>'
     # ./spec/support/database_cleaner.rb:18:in `block (2 levels) in <top (required)>'

  2) routes for users controller development environment development routes exist
     Failure/Error: expect(:post => "/ims/users/create_development_account").to be_routable
       expected {:post=>"/ims/users/create_development_account"} to be routable
     # /var/code/bundle/ruby/2.2.0/gems/given_core-3.5.4/lib/given/rspec/monkey.rb:21:in `handle_matcher'
     # ./spec/routing/users_controller_spec.rb:18:in `block (3 levels) in <top (required)>'
     # ./spec/support/database_cleaner.rb:18:in `block (2 levels) in <top (required)>'

Solution

  • Routes are loaded once on app start, and Rails.env will be 'test'.

    Usually it's better to keep you development environment as close to production as possible, including routes.

    If you want some shortcuts in dev env, there're options:

    1. You can use regular /ims/users/new and create actions with a parameter that will only have effect in development. For example /ims/users/new?development=true that will also render additional hidden field to pass state to create
    2. Use dynamic constraints on routes (this way routes will still be listed in production/test, but not accessible)
    3. Just leave the routes as always present and raise an error in controller for wrong environments
    4. Extract whole development_account-feature into an engine, test separately and mount it only in development (the hardest way, not always possible)

    I'd go with first option, it's both simple, incapsulates relatively well and most probably your *_development_account mimic corresponding actions anyway