在Rails 3中测试子域约束路由

时间:2011-06-12 18:02:31

标签: ruby-on-rails testing subdomain

我正在用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. 哪里一般是什么路线?
  2. 他们如何对此特殊情况进行测试?
  3. 我想尝试不同的方法(Capybara和朋友),但我不想切换我的测试框架(已经有我的RSpec时间),因为我对Test :: Unit非常满意。 / p>

    提前致谢,亲切的问候!

9 个答案:

答案 0 :(得分:13)

实际上解决方案非常简单,因为assert_routing 支持网址:

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提供网址的能力已添加here

搜索策略:

  • 发现assert_routing已在actionpack
  • 中定义
  • gem install gemedit
  • gem edit actionpack
  • 已打开lib/action_dispatch/testing/assertions/routing.rb,发现recognized_request_for处理路径处理
  • 通过GitHub打开文件,并选择“责备”以查看添加该功能的提交

答案 1 :(得分: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

以下是一些讨论此主题的链接,您可能会觉得有用:

祝你好运。

答案 2 :(得分:2)

我找到了非常糟糕的方法来修补路线测试机器而没有找到任何方法。

我最终只是扼杀了约束'匹配?方法:

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。我喜欢在我的控制器规范中放置路由测试,就在操作的描述块之前。共享示例将被移动到一个混合到控制器规范中的模块。)

答案 3 :(得分:1)

为了让我的约束在开发模式下运行,我向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

这适用于域名约束,它对子域约束同样有效。

答案 4 :(得分:1)

这些其他答案都没有回答这个问题,至少没有任何优雅的方式。不需要模拟。

在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。

答案 5 :(得分:0)

我自己是@ RoR3的新手,但也许这个截屏会帮助你:Railscasts Subdomain

答案 6 :(得分:0)

我会测试你班级的功能,不一定是路线本身。我觉得这基本上是测试rails。

您知道如果您将类传递给约束块,那么如果您定义了.matches,它将会起作用吗?方法。所以只要测试你在那里得到了预期的逻辑。

答案 7 :(得分:0)

正如亚特·波利托上面所说,测试你想要发生的事情。

在你的约束中,你必须有某种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响应:成功,我认为。

答案 8 :(得分:-3)

rails 3中的子域名非常简单,它只是用作:

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