Как мне написать тест контроллера двигателя Rails 3.1 в rspec? - PullRequest
45 голосов
/ 05 марта 2011

Я написал движок Rails 3.1 с пространством имен Posts. Следовательно, мои контроллеры находятся в app / controllers / posts /, мои модели в app / models / posts и т. Д. Я могу протестировать модели просто отлично. Спецификация для одной модели выглядит так ...

module Posts
  describe Post do
    describe 'Associations' do
      it ...
      end

... и все работает отлично.

Однако спецификации для контроллеров не работают. Движок Rails монтируется в / posts, но контроллером является Posts :: PostController. Таким образом, тесты ищут маршрут контроллера для сообщений / сообщений.

  describe "GET index" do
    it "assigns all posts as @posts" do
      Posts::Post.stub(:all) { [mock_post] }
       get :index
       assigns(:posts).should eq([mock_post])
    end
  end

что дает ...

  1) Posts::PostsController GET index assigns all posts as @posts
     Failure/Error: get :index
     ActionController::RoutingError:
     No route matches {:controller=>"posts/posts"}
     # ./spec/controllers/posts/posts_controller_spec.rb:16

Я испробовал всевозможные трюки в файле маршрутов тестового приложения ...: пространство имен и т. Д., Но безрезультатно.

Как мне заставить это работать? Похоже, что этого не произойдет, поскольку движок устанавливает контроллер в / posts, а пространство имен помещает контроллер в / posts / posts для целей тестирования.

Ответы [ 6 ]

42 голосов
/ 29 апреля 2011

Я предполагаю, что вы тестируете свой двигатель с приложением фиктивных рельсов, например, сгенерированным enginex .

Ваш двигатель должен быть установлен в фиктивном приложении:

В spec/dummy/config/routes.rb:

Dummy::Application.routes.draw do
  mount Posts::Engine => '/posts-prefix'
end

Мое второе предположение состоит в том, что ваш двигатель изолирован:

В lib/posts.rb:

module Posts
  class Engine < Rails::Engine
    isolate_namespace Posts
  end
end

Я не знаю, действительно ли необходимы эти два предположения, но так устроен мой собственный движок.

Обходной путь довольно прост вместо этого

get :show, :id => 1

используйте это

get :show, {:id => 1, :use_route => :posts}

Символом :posts должно быть название вашего двигателя, а НЕ путь, на котором он установлен.

Это работает, потому что параметры метода get передаются прямо в ActionDispatch::Routing::RouteSet::Generator#initialize (определено здесь ), которое, в свою очередь, использует @named_route для получения правильного маршрута из Rack::Mount::RouteSet#generate (см. здесь и здесь ).

Погружаться во внутренние рельсы - это весело, но довольно много времени, я бы не делал это каждый день ;-).

НТН

22 голосов
/ 05 октября 2011

Я обошел эту проблему, переопределив предоставляемые методы get, post, put и delete, чтобы они всегда передавали use_route в качестве параметра.

Я использовал ответ Бенуа в качестве основы для этого.Спасибо, приятель!

module ControllerHacks
  def get(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "GET")
  end

  # Executes a request simulating POST HTTP method and set/volley the response
  def post(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "POST")
  end

  # Executes a request simulating PUT HTTP method and set/volley the response
  def put(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "PUT")
  end

  # Executes a request simulating DELETE HTTP method and set/volley the response
  def delete(action, parameters = nil, session = nil, flash = nil)
    process_action(action, parameters, session, flash, "DELETE")
  end

  private

  def process_action(action, parameters = nil, session = nil, flash = nil, method = "GET")
    parameters ||= {}
    process(action, parameters.merge!(:use_route => :my_engine), session, flash, method)
  end
end

RSpec.configure do |c|
  c.include ControllerHacks, :type => :controller
end
18 голосов
/ 09 июля 2013

Используйте директиву rspec-rails routes:

describe MyEngine::WidgetsController do
  routes { MyEngine::Engine.routes }

  # Specs can use the engine's routes & named URL helpers
  # without any other special code.
end

- Официальные документы RSpec Rails 2.14 .

5 голосов
/ 13 марта 2013

На основе этого ответа я выбрал следующее решение:

#spec/spec_helper.rb
RSpec.configure do |config|
 # other code
 config.before(:each) { @routes = UserManager::Engine.routes }
end

Дополнительное преимущество заключается в том, что вам не нужно иметь before(:each) блок в каждом контроллере-спецификации.

2 голосов
/ 20 ноября 2012

Решение проблемы, когда у вас нет или вы не можете использовать isolate_namespace:

module Posts
  class Engine < Rails::Engine
  end
end

В спецификациях контроллера для исправления маршрутов:

get :show, {:id => 1, :use_route => :posts_engine}   

Rails добавляет _engine кМаршруты вашего приложения, если вы не используете isolate_namespace.

0 голосов
/ 25 мая 2012

Я разрабатываю драгоценный камень для своей компании, который предоставляет API для приложений, которые мы запускаем. Мы все еще используем Rails 3.0.9, с последней версией Rspec-Rails (2.10.1). У меня была похожая проблема, в которой я определил маршруты, как в моем движке Rails.

match '/companyname/api_name' => 'CompanyName/ApiName/ControllerName#apimethod'

Я получаю сообщение об ошибке типа

ActionController::RoutingError:
 No route matches {:controller=>"company_name/api_name/controller_name", :action=>"apimethod"}

Оказывается, мне просто нужно было переопределить мой маршрут в случае подчеркивания, чтобы RSpec мог соответствовать ему.

match '/companyname/api_name' => 'company_name/api_name/controller_name#apimethod'

Я полагаю, что в тестах контроллера Rspec используется обратный поиск, основанный на регистре подчеркивания, тогда как Rails установит и интерпретирует маршрут, если вы определите его в регистре верблюдов или подчеркивании.

...