Тестирование ограничений по субдоменам в Rails 3
Я тестирую свои приложения Rails с помощью Test:: Unit. Проблема, с которой я часто сталкиваюсь, - это тестирование моих маршрутов приложений, для которых я еще не нашел решение.
В настоящий момент я работаю над приложением, которое использует поддомены стиля Basecamp для дифференциации учетных записей.
Существуют маршруты, для которых требуется субдомен
constraints(SubdomainRoute) do
get "/login" => "user_sessions#new", :as => :login
get "/logout" => "user_sessions#destroy", :as => :logout
...
end
а также маршруты, к которым можно получить доступ только без субдомена
constraints(NoSubdomainRoute) do
match "/" => "public#index", :as => :public_root
match "/signup" => "public#signup", :as => :signup
...
end
Класс SubdomainRoute определяется как:
class SubdomainRoute
def self.matches?(request)
request.subdomain.present? && request.subdomain != "api" && request.subdomain != "www"
end
end
Класс NoSubdomainRoute в значительной степени делает обратное.
Маршрутизация работает, как и ожидалось, но как проверить те, которые используются с помощью Test:: Unit?
В функциональных тестах я могу сделать что-то вроде
assert_routing "/signup", :controller => "public", :action => "signup"
но я не могу предоставить субдомен, так что на самом деле это только проверка Rails внутренности, которая ничего не добавляет к тестированию моего приложения. То, что я хочу протестировать в этом случае, - это то, что signup_path/signup_url доступен с субдоменом или без него.
В коде что-то вроде этого
assert_raise(ActionDispatch::RoutingError) { get "http://account.test.host/signup" }
get "http://www.test.host/signup"
assert_response :success
get не работает в этом случае, потому что Test:: Unit обрабатывает весь URL как действие контроллера (...: action = > "http://account.test.host/signup" ).
Настройка
@request.host = "subdomain.test.host"
влияет только на ваш внутренний код контроллера (например, получение текущей учетной записи путем извлечения субдомена с хоста), но в этом случае не влияет на маршрутизацию.
Я не знаю, отличается ли ситуация в тестах интеграции.
Итак, два основных вопроса:
- Где - маршруты, предназначенные для тестирования в целом?
- Как они тестируются в этом специальном случае?
Я собираюсь попробовать разные подходы (Capybara и друзья), но я не хочу переключать свою платформу тестирования (уже имел мой RSpec-время), так как я в остальном очень доволен Test:: Unit.
Спасибо заранее и любезны!
Ответы
Ответ 1
На самом деле решение очень просто, поскольку assert_routing
поддерживает URL-адреса:
assert_routing "http://subdomain.example.com/login",
{ :controller => "user_sessions", :action => "new" }
assert_routing "http://www.example.com/signup",
{ :controller => "public", :action => "signup" }
Возможность предоставления URL-адреса assert_routing была добавлена здесь.
Стратегия поиска:
- Найдено
assert_routing
был определен в actionpack
-
gem install gemedit
-
gem edit actionpack
- Открыто
lib/action_dispatch/testing/assertions/routing.rb
и найдено recognized_request_for
- это то, что обрабатывает обработку пути
- Открыл файл через GitHub и выбрал 'Blame', чтобы узнать, какая команда добавила эту функциональность
Ответ 2
Я нашел rr, чтобы лучше всего тестировать субдомены и настраиваемые домены. Например, у меня есть приложение для стойки, которое управляет настраиваемым доменом. Вот пример теста:
require File.join(File.dirname(__FILE__), '..', 'test_helper')
require 'rr'
require 'custom_domain'
require 'rack/test'
class CustomDomainTest < ActiveSupport::TestCase
include Rack::Test::Methods
include RR::Adapters::TestUnit
def app
Rails.application
end
def test_cname_to_subdomain
mock(CustomDomain::Cname).resolver('www.example.com', '.lvh.me') { 'subdomain.lvh.me' }
get 'http://www.example.com:80/users'
assert_equal 'www.example.com, subdomain.lvh.me:80', last_request.env['HTTP_X_FORWARDED_HOST']
assert_equal 'www.example.com', last_request.env['SERVER_NAME']
assert_equal 'www.example.com', last_request.env['X_CUSTOM_CNAME']
assert_equal 'subdomain.lvh.me', last_request.env['X_CUSTOM_SUBDOMAIN']
end
end
end
И вот несколько ссылок, обсуждающих эту тему, которые могут вам пригодиться:
Удачи.
Ответ 3
Я искал нехорошие пути для исправления в машинах тестирования маршрутов и не нашел.
В итоге я закончил матч с ограничениями? Методы:
describe ThingsController do
shared_examples_for "a subdomain route" do |http_method, path, expected_action|
context("on a subdomain") do
before do
stub(SubdomainRoute).matches? { true }
stub(NoSubdomainRoute).matches? { false }
end
it { should route(http_method, path).to(:action => expected_action) }
end
context("on the main domain") do
before do
stub(SubdomainRoute).matches? { false }
stub(NoSubdomainRoute).matches? { true }
end
it { should_not route(http_method, path).to(:action => expected_action) }
end
end
it_should_behave_like "a subdomain route", :get, '/things/new', :new
...
(Я использую rspec и rr. Мне нравится ставить тесты маршрута в своих спецификациях для контроллера, как раз перед описанием блоков для действий. Общие примеры будут перенесены в модуль, который будет смешан в спецификации контроллера.)
Ответ 4
Чтобы мои ограничения выполнялись в режиме разработки, я добавляю значение в хэш ENV, начиная с сервера:
site=trivial.ly ruby script/rails server
Здесь я передаю весь домен, вместо этого вы можете передать субдомен. Затем в моем классе ограничений я обнаруживаю либо домен, либо значение ENV [: site], например:
class DomainConstraint
def initialize(domain)
@domains = [domain].flatten
end
def matches?(request)
@domains.include?(request.domain) || @domains.include?(ENV["site"])
end
end
Тестирование контроллера с конкретным ограничением теперь просто заключается в том, чтобы установить правильное значение ENV [: site] как это (здесь в test:: unit):
require 'test_helper'
class TrivialLy::SplashPagesControllerTest < ActionController::TestCase
test "should get android splash page" do
ENV["site"] = "trivial.ly"
get :android
assert_response :success
end
end
Это работает для доменных ограничений, оно будет одинаково хорошо работать для ограничений поддоменов.
Ответ 5
Ни один из этих ответов не отвечает на вопрос, по крайней мере, не изящно. Моксы не нужны.
Чтобы использовать ограничения поддоменов Rails 3 в тесте интеграции Rails: просто включите домен как часть запроса в свой интеграционный тест:
get "http://admin.example.com/dashboard"
Я успешно тестировал этот (по большому счету) следующий маршрут без проблем:
scope :admin, as: 'admin', module: 'admin' do
constraints subdomain: 'admin' do
resource 'dashboard', controller: 'dashboard'
end
end
Возможно, обманщик не использовал интеграционные тесты или более старую версию Rails.
Ответ 6
Я сам новый @RoR3, но, возможно, этот скринкаст поможет вам: Поддомен Railscasts
Ответ 7
Я бы тестировал функциональность вашего класса, не обязательно сам маршрут. Я чувствую, что это по существу тестирование рельсов.
Вы знаете, что если вы передадите свой класс блоку ограничений, он будет работать, если вы определили .matches? метод. Поэтому просто проверьте, что вы получаете ожидаемую логику.
Ответ 8
Как сказал выше, матовый Полито, проверьте, что вы хотите.
В ваших ограничениях вы должны иметь какой-то файл before_filter или что-то, что обрабатывает случай, когда кто-то не должен быть там, где он есть. или даже как www.yourdomain.com/login
, вы должны обработать эту ситуацию с помощью перенаправления и предупреждения о вспышке, чтобы вы могли протестировать это.
все еще, в моем случае для поддоменов, я назначил переменную в почтовом ящике с субдоменом. Это была переменная, которую я проверил в своем тесте, например:
setup do
# get_sub is just @request.host = "#{sub}.local.me" in test_helper.rb
get_sub("two")
@deal = deals(:two)
end
test "should get show" do
get :show, :token => @deal.token
assert_response :success
["deal", "subdomain", "user_profile"].each do |variable|
assert assigns[variable.to_sym], "I can't find a var called #{variable}"
end
end
поэтому проверяем, что все, что я хочу, работает, и передает sub. в вашем случае это должно быть только то, что /login отвечает: успех, я думаю.
Ответ 9
Субдомены в рельсах 3 очень просты, он просто помещается как:
constraints :subdomain => subdomain_name do
#here comes all routes which routes under above subdomain
end