【问题标题】:Testing subdomain constrained routes in Rails 3在 Rails 3 中测试子域约束路由
【发布时间】:2011-06-12 18:02:31
【问题描述】:

我正在使用 Test::Unit 测试我的 Rails 应用程序。我经常遇到的一个问题是测试我尚未找到解决方案的应用程序的路由。

目前,我正在开发一个使用 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"

仅对您的内部控制器代码有影响(例如,通过从主机中提取子域来获取当前帐户),但在这种情况下不影响路由。

我不知道在集成测试中情况是否有所不同。

所以两个主要问题是

  1. 一般来说路线应该是 testet 吗?
  2. 他们如何针对这种特殊情况进行了测试?

我愿意尝试不同的方法(Capybara 和朋友),但我不想切换我的测试框架(已经有我的 RSpec-time),因为我对 Test::Unit 非常满意。

在此先感谢您的问候!

【问题讨论】:

    标签: ruby-on-rails testing subdomain


    【解决方案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" }
    

    添加了向 assert_routing 提供 URL 的功能 here

    搜索策略:

    • 发现assert_routingactionpack 中定义
    • 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 集成测试中使用 Rails 3 子域约束:只需将域作为请求的一部分包含在集成测试中:

          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 Subdomain

            【讨论】:

            • 感谢您的回复,但我的子域已经开始工作了 - 我的很多功能都来自 Ryan,但不幸的是,他在此截屏视频中没有丢失任何关于测试的信息。
            【解决方案7】:

            我会测试你的类的功能,不一定是路由本身。我觉得这本质上是在测试 Rails。

            你知道如果你将你的类传递给约束块,如果你定义了 .matches 它将起作用?方法。因此,只需测试您是否获得了预期的逻辑。

            【讨论】:

              【解决方案8】:

              正如上面的 matt Polito 所说,测试你想要发生的事情。

              在您的约束中,您必须有某种 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
              

              所以测试我想要的任何东西,并且正在通过子。在您的情况下,我认为应该只是 /login 以 :success 响应。

              【讨论】:

                【解决方案9】:

                rails 3 中的子域非常简单,只需将其表示为:

                constraints :subdomain => subdomain_name do
                  #here comes all routes which routes under above subdomain
                end
                

                【讨论】:

                • OP 询问如何测试子域,而不是创建子域。
                猜你喜欢
                • 2012-09-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-06-17
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多