Rspec-rails: Rspec testing with subdomain constraint

Created on 18 Jul 2013  路  12Comments  路  Source: rspec/rspec-rails

I've been stuck on this issue for almost two days now; I've seen literally all answers on stackoverflow and none work for me. I'm running on Rails 3.

I have an api subdomain that I'm trying to test the controllers in. The controllers are in app/controllers/v1. The route I am trying to test is: GET /v1/payments for the index action.

So far, I am using http://api.lvh.me:3000/v1/payments when running the requests. In rspec though, since I have a subdomain, it keeps throwing at me a No Route Found error like this:

Failure/Error: get :index
     ActionController::RoutingError:
       No route matches {:controller=>"v1/payments"}

I've added api.example.com to my /etc/hosts and I've added each of the following to a before block (each line separately, not all together obviously):

request.host = 'api.example.com'
request.host = 'api.example.com:3000'
request.host = 'api.example.com:3000/v1/payments'
request.host = 'api.lvh.me:3000'
request.host = 'api.lvh.me:3000/v1/payments'

They all failed with the same error. I even added request.path = 'v1/payments' as that's always set in the development environment but that also failed.

How am I getting a No Route Error? Is it because I only see :controller => and no :action =>? Or is it just completely not getting the subdomain?

I did add api.example.com to /etc/hosts, so that should not be an issue when I add request.host = 'api.example.com', right?

In the requests/ integration tests, if I do: get "api.lvh.me:3000/v1/payments", it works. Can I specify the path directly in controllers tests? If not, how else can I fix this issue?

Any help will be greatly appreciated!

Thanks,

Most helpful comment

Did you try get :index, subdomain: 'api'?

All 12 comments

Can you post the relevant parts of your config/routes.rb file? I don't know offhand how to invoke controllers in a subdomain situation, but I'd be happy to try it out and document what I find.

Sure, thanks! There you go:

Client::Application.routes.draw do
  constraints :subdomain => "api" do
    api_version(:module => "V1", :path => {:value => "v1"}, :defaults => {:format => :json}, :default => true) do
      match '/payments' => 'payments#create', :via => :post
      match '/payments' => 'payments#index', :via => :get
      match '/payments/:id' => 'payments#show', :via => :get
      match '/payments/:id/refund' => 'payments#refund', :via => :post

      match '/customers' => 'customers#create', :via => :post
      match '/customers' => 'customers#index', :via => :get
      match '/customers/:id' => 'customers#show', :via => :get
      match '/customers/:id' => 'customers#update', :via => :patch
      match '/customers/:id' => 'customers#delete', :via => :delete

      match '/accounts' => 'accounts#create', :via => :post
      match '/accounts/login' => 'accounts#login', :via => :post
      match '/accounts/logout' => 'accounts#logout', :via => :delete
    end
  end
  devise_for :accounts
  resources :accounts
  match "/404" => "errors#not_found"
  match "/500" => "errors#exception"
end

Did you try get :index, subdomain: 'api'?

I still get an error: No route matches {:subdomain=>"api", :controller=>"v1/payments"}

:( I'm starting to give up on testing all-together. Rspec is impossible and its documentation never mentions subdomains. I can't believe something as common as testing within a subdomain is that difficult.

rspec-rails is a thin wrapper around Rails own test harness, so the short coming here is that if Rails doesn't document how to do something, then we're not likely to either. That said I'm able to get a controller test working with a similar route set with no trouble what so ever, api_version is coming from Devise? or something else? Because that's breaking for me.

If you can post the spec you're attempting to run that would help.

I guess that makes sense. api_version is coming from versionist, an API-versioning gem which can be found at https://github.com/bploetz/versionist.

So here's the setup: my controllers are in app/controllers/v1. I am trying to test a simple GET index route/action. My spec is in spec/controllers/v1 as follows:

module V1
  describe PaymentsController do
    describe "GET #index" do
      it "does something" do
        get :index
      end
    end
  end
end

And I get the same ActionController::RoutingError: No route matches {:controller=>"v1/payments"} error.

I would really really appreciate it if you can help me.

Versionist recommends putting header strategy versioning (sending API version as a header) in spec/requests as controller tests bypasses most middleware. I am using regular path strategy /v1/ in the urlso I wouldn't have to do that. Furthermore, I do not want to usespec/requests` as devise cannot be used in there during tests so I won't be able to pass anything (I cannot sign in a user etc.).

Try changing: api_version(:module => "V1", ...) to api_version(:module => "v1", ...)

I don't really know much about versionist, but it seems to work for me :/

Wow! This works! That's funny because my modules are still V1 (changing them to v1 fails). But thanks! I have a different error now but I can sort it out; doesn't seem impossible as this.

Thanks a lot!

:) Glad I could help.

I suspect that something doing the constantizing is mismatching, try raising an issue on versionist, as basically something it was generating was making the routes not match up

For future googlers:

I ended up here trying to solve an ActionController::RoutingError in my request tests.

I'm using an :api namespace in my routes.rb like so:

  namespace :api, :path => "", :constraints => { :subdomain => "api" }, defaults: { format: :json } do
    #some routes
  end

  devise_for :users,
              :constraints => { :subdomain => "api" },
              defaults: { format: :json },
              path: '',
              path_names: {
                # some path names
              },
              controllers: {
                # some controllers
              }

The above suggestions didn't work for me. However the solution came from this StackOverflow answer. I just added the line before { host! "api.example.com" } in my tests:

require 'rails_helper'

RSpec.describe 'Users', type: :request do
  before { host! "api.example.com" }

  describe 'GET /users' do
    context 'authenticated' do
      #some test
    end
  end

  #some more tests
end

And that did the trick.

For future googlers:

I ended up here trying to solve an ActionController::RoutingError in my request tests.

I'm using an :api namespace in my routes.rb like so:

  namespace :api, :path => "", :constraints => { :subdomain => "api" }, defaults: { format: :json } do
    #some routes
  end

  devise_for :users,
              :constraints => { :subdomain => "api" },
              defaults: { format: :json },
              path: '',
              path_names: {
                # some path names
              },
              controllers: {
                # some controllers
              }

The above suggestions didn't work for me. However the solution came from this StackOverflow answer. I just added the line before { host! "api.example.com" } in my tests:

require 'rails_helper'

RSpec.describe 'Users', type: :request do
  before { host! "api.example.com" }

  describe 'GET /users' do
    context 'authenticated' do
      #some test
    end
  end

  #some more tests
end

And that did the trick.

Was this page helpful?
0 / 5 - 0 ratings