Подтвердить что ты не робот

Тестирование ограничений по субдоменам в 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.

Спасибо заранее и любезны!

4b9b3361

Ответ 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