Использование url_for в спецификации Rails/Capybara/Poltergeist отправляет драйвер на сайт example.com вместо приложения.

Если я вызову url_for в спецификации функции, она вернет абсолютный URL-адрес, начинающийся с http://www.example.com/. Capybara с радостью попытается загрузить страницы на этом сайте, но это не имеет ничего общего с моим приложением. Вот минимальные шаги для воспроизведения проблемы:

Начните с этого Gemfile:

source 'https://rubygems.org'

gem "sqlite3"
gem "jquery-rails"
gem "draper"
gem "rails", '4.1.0'
gem "therubyracer"
gem "uglifier"
gem "rspec-rails"
gem "capybara"
gem "poltergeist"
gem "launchy"

Запустите следующее:

bundle
rails new myapp -O
cd myapp
rm Gemfile Gemfile.lock
rails generate controller Test test
rails generate rspec:install
mkdir spec/features

Закомментируйте строки в spec/spec_helper.rb, в которых говорится, что их следует удалить, если ActiveRecord не используется, а затем создайте spec/features/feature_spec.rb со следующим содержимым:

require 'capybara/poltergeist'

Capybara.configure do |config|
  config.javascript_driver = :poltergeist
end

require 'spec_helper'

describe "nothing", js: true do
  specify do
    visit(url_for(controller: :test, action: :test))
    save_and_open_page
  end
end

Наконец, запустите rake spec, и вы увидите всплывающую страницу example.com в браузере. Я проверил это поведение на Rails 3.2.17.

Почему это происходит, и есть ли способ получить URL тестируемого приложения вместо example.com?

Редактировать: кое-что, что я обнаружил, изучая это подробнее:

ActionDispatch::Routing::UrlFor.url_for вызывается из примеров RSpec. По умолчанию only_path имеет значение false.

ActionView::RoutingUrlFor — это версия, которую вы получаете, скажем, в представлении. По умолчанию only_path имеет значение true, что работает намного лучше.

Эта фиксация в гем rspec-rails, вероятно, вызвала проблему, добавив www.example.com как хост по умолчанию. Нигде нет объяснения, почему этот хост является подходящим/полезным выбором.


person pdg137    schedule 18.04.2014    source источник
comment
Эта проблема GitHub с полтергейстом связана: github.com/teampoltergeist/poltergeist/issues/201   -  person pdg137    schedule 22.04.2014
comment
В связи с этим я также заметил эту проблему при использовании динамического URL-адреса в спецификации функции с javascript (например, visit thing_url). Я нашел github.com/jnicklas/capybara/issues/306, где jeromelefeuvre говорит, что thing_path следует использовать вместо thing_url. Я перешел на версию _path и она работает.   -  person robertwbradford    schedule 29.04.2014


Ответы (2)


Проблема проявляется по следующим причинам:

  1. Вы используете Poltergeist, который использует PhantomJS, который прекрасно открывает любой URL-адрес.
  2. Вы используете помощник url_for, который должен знать домен, для которого он должен генерировать URL-адрес. При использовании внутри представления или контроллера Rails Rails предоставляет домен на основе того, что использовалось для выполнения запроса. Находясь вне представления или контроллера, как в тестах ActionMailer или Capybara, домен неизвестен. Capybara по умолчанию устанавливает неизвестный домен на example.com.

Так что все работает как надо. Теперь это происходит не так, как вы хотите, чтобы это работало. Однако, если вы хотите, чтобы это работало так, как вы хотели бы, вы должны сделать одну из следующих вещей:

  1. Используйте параметр path_only в url_for, чтобы запретить использование хост-части.
  2. Используйте параметр host в url_for, чтобы указать правильный хост.
person Alex Peachey    schedule 23.04.2014
comment
Это неудовлетворительно, потому что в представлении или контроллере only_path по умолчанию имеет значение true, и все работает так, как задумано. Но в спецификации он изменен на значение по умолчанию false. Почему в спецификациях существует такое особое поведение, которое, кажется, может только что-то сломать? Да, в этом простом примере я, безусловно, мог бы добавить only_path: true, но в конечном счете я хотел бы иметь возможность вызывать свои собственные вспомогательные методы, которые, в свою очередь, вызывают url_for; что я вынужден сделать, чтобы реализовать вашу стратегию, так это написать альтернативные версии этих методов только для использования в тестировании. - person pdg137; 23.04.2014
comment
Я голосую, потому что это лучшее решение, о котором я знаю. Но я надеюсь на то, что решит или, по крайней мере, объяснит основную проблему, заключающуюся в том, что спецификации Rails вставляют явно бесполезный example.com в URL-адреса. - person pdg137; 23.04.2014
comment
Как я уже упоминал, only_path по умолчанию имеет значение false. Это не так по умолчанию. В представлении или элементе управления это работает, потому что Rails понимает контекст запроса (домен, хост и т. д.). Когда вы находитесь вне контекста реального запроса, вы должны что-то с этим делать. Никогда не повредит четко указать свои варианты, даже если вы делаете помощника. Я бы также подумал об использовании url_for less в целом. Хорошо определенный файл маршрутов и стандартные помощники пути должны устранить необходимость использования url_for. - person Alex Peachey; 23.04.2014
comment
Параметры и значения по умолчанию см. в документации: apidock.com/rails/ActionDispatch/Routing/ URLFor/url_for - person Alex Peachey; 23.04.2014
comment
Ваша ссылка ведет на версию url_for, вызываемую из спецификаций. Я имею в виду, что это отличается от того, что обычно используется в приложении, которое имеет истинное значение по умолчанию: apidock.com/rails/ActionView/RoutingUrlFor/url_for - person pdg137; 24.04.2014
comment
Я хотел бы, чтобы мои именованные маршруты работали лучше, но это другая тема, и я думаю, что это может быть невозможно или, по крайней мере, очень грязно. На данный момент я хотел бы понять, почему url_for, используемый в спецификациях, кажется сломанным, и если для этого нет причин, я надеюсь, что смогу помочь это исправить. - person pdg137; 24.04.2014
comment
То, на что вы ссылаетесь, является частью ActionView. В спецификации запроса у вас нет представления или доступа к одному или любому типу контекста запроса. Вы не можете использовать эту версию в своих спецификациях. Это хороший аналог того, почему вам нужно установить свой хост/домен в ActionMailer. Я считаю, что причина, по которой это так, заключается в том, что внутри ActionView можно предположить, что вы находитесь в контексте запроса, и просто наличие относительного пути было бы не только возможным, но и желательным. Вне ActionView при использовании версии ActionDispatch вы не можете предполагать такие вещи, поэтому по умолчанию установлено значение false. - person Alex Peachey; 24.04.2014

Вот как работают эти драгоценные камни. http://example.com не имеет отношения к вашему приложению. Как правило, в вашем приложении не должно быть полностью жестко запрограммированных путей. Rails пытается определить ваш локальный домен (в спецификациях это example.com, который настраивается) и создает от него пути.

Идея заключается в том, что у вас есть базовый URL-адрес, который может измениться. Скажем, для постановки я использую локальное приложение Heroku: randomname-123-staging.heroku.com. Мои URL-адреса будут иметь префикс с этим. Однако в производстве у меня есть доменное имя. Мои URL там будут начинаться с mydomain.com. Для меня нет смысла обновлять все мои URL-адреса на основе базового домена среды; это должно быть (и есть) предоставлено Rails.

С помощью общего домена, который гарантированно не разрешается в реальный IP-адрес , спецификации помогут вам закодировать эту возможность.

person Aaron K    schedule 19.04.2014
comment
Если это не имеет отношения к моему приложению, почему Полтергейст не может загрузить URL-адреса, сгенерированные url_for, как показывает мой пример? Что еще более важно, как мне исправить эту ситуацию? - person pdg137; 21.04.2014
comment
Ваша ссылка не поддерживает утверждение о том, что example.com гарантированно не разрешается в реальный IP-адрес, и даже если бы это было так, это не помогло бы ситуации. - person pdg137; 21.04.2014
comment
@ pdg137 Capybara обычно ожидает стоечное приложение: github.com/jnicklas/capybara#calling -удаленные-серверы; который игнорирует домен. Использование save_and_open_page обходит это и позволяет браузеру вместо этого выполнять поиск DNS. Вы можете попробовать установить Capybara.app_host на localhost или эквивалентную настройку. - person Aaron K; 21.04.2014
comment
Если save_and_open_page удалить, пройдут ли спецификации? - person Aaron K; 21.04.2014
comment
Я использую полтергейст, который поддерживает удаленные серверы, поэтому я не понимаю, что это (очень желательно!) игнорирует функцию домена, а установка Capybara.app_host не влияет на возвращаемое значение url_for, поскольку этот метод вообще не взаимодействует с Capybara. . Спецификации в моем примере проходят, так как я ничего не тестировал, но если вы напишете какие-нибудь полезные тесты, они вылетят с сообщением вида: expected to find text "test" in "Example Domain This domain is established to be used for illustrative..." - person pdg137; 22.04.2014