我在使用capybara-webkit进行集成测试时遇到问题。该问题与使用子域直接相关。
我的所有正常(非JS)集成测试都在运行,但我无法弄清楚如何让它为多租户应用程序工作。 我正在使用公寓gem进行多租户,它使用PostgreSQL模式来分割用户数据。用户可以注册,选择子域,然后访问该应用程序。
应用程序设置如下:
routes.rb
中,我有一些限制,具体取决于子域是否存在(查看是否request.subdomain.present?
)。如果没有子域存在,则唯一可用的路由是resources :accounts, only: [:new, :create]
,否则其他应用程序路由可用。我的accounts_controller上有一个before_action来调用私有方法load_schema切换到当前子域或重定向到root_url。
def load_schema
Apartment::Tenant.switch!('public')
return unless request.subdomain.present?
if current_account
Apartment::Tenant.switch!(current_account.subdomain)
else
redirect_to root_url(subdomain: false)
end
end
def current_account
@current_account ||= Account.find_by(subdomain: request.subdomain)
end
helper_method :current_account
在我的所有RSpec功能规范(当前正在传递的不使用JavaScript)中,我运行了sign_user_in方法来签署用户:
def sign_user_in(user, opts={})
if opts[:subdomain]
visit new_user_session_url(subdomain: opts[:subdomain])
else
visit new_user_session_path
end
fill_in 'user[email]', with: user.email
fill_in 'user[password]', with: (opts[:password] || user.password)
click_button 'Log in'
end
我的所有规格都通过了这种方法。
但是,当我使用:js on运行规范时,它永远无法找到'user [email]'。视图使用引导模式弹出创建或编辑操作,然后使用AJAX(通过remote:true)添加或更新页面上新创建的资源。我目前试图通过的一个简单的功能规范是:
require 'rails_helper'
feature 'user creates product' do
let(:user) { build(:user) }
let(:account) { create(:account_with_schema, owner: user) }
scenario 'successfully', :js do
product = build_stubbed(:product, name: 'Test Product', amazon_sku: 'test_sku', price: 9.99)
sign_user_in(user, subdomain: account.subdomain)
click_on 'Products'
click_on 'New Product'
fill_in 'product[name]', with: product.name
fill_in 'product[amazon_sku]', with: product.amazon_sku
fill_in 'product[price]', with: product.price
click_on 'Create Product'
expect(page).to have_content('Test Product')
end
end
在我的日志中,它显示它正在尝试连接到subdomain1.example.com/users/sign_in:
Received "Visit(http://subdomain1.example.com/users/sign_in)"
Started "Visit(http://subdomain1.example.com/users/sign_in)"
Load started
"Visit(http://subdomain1.example.com/users/sign_in)" started page load
Started request to "http://subdomain1.example.com/users/sign_in"
Finished "Visit(http://subdomain1.example.com/users/sign_in)" with response "Success()"
Received 200 from "http://subdomain1.example.com/users/sign_in"
此子域名1基于我对我的帐户工厂的序列,例如:
factory :account do
sequence(:subdomain) { |n| "subdomain#{n}" }
...
当然这会失败,因为它无法连接到subdomain1.example.com:
Failures:
1) user creates product successfully
Failure/Error: fill_in 'user[email]', with: user.email
Capybara::ElementNotFound:
Unable to find field "user[email]"
当我在本地测试时,我使用lvh.me在本地测试子域(因为你不能在localhost上做子域)。
由于我看到了这些错误,因此我将以下内容添加到我的rails_helper.rb中:
Capybara::Webkit.configure do |config|
config.debug = true
config.allow_unknown_urls
config.allow_url("lvh.me")
config.allow_url("*.lvh.me")
end
但这仍然导致同样的事情。经过一些谷歌搜索,我发现一个问题,提到使用“路径”与“网址”。例如,在我的sign_user_in帮助
中而不是:
if opts[:subdomain]
visit new_user_session_url(subdomain: opts[:subdomain])
else
使用:
if opts[:subdomain]
visit new_user_session_path(subdomain: opts[:subdomain])
else
当我这样做时,我似乎越来越近了,因为日志现在显示了这个:
Received "AllowUrl(*.lvh.me)"
Started "AllowUrl(*.lvh.me)"
Finished "AllowUrl(*.lvh.me)" with response "Success()"
Wrote response true ""
Received "Visit(http://lvh.me:3000/users/sign_in)"
Started "Visit(http://lvh.me:3000/users/sign_in)"
很酷,进步。虽然它不包括子域,即使它正在被传入。另外,这会导致其他5个测试因路由错误而全部失败:
例如:
5) user authentication does not allow user from one subdomain to sign in on another subdomain
Failure/Error: visit new_user_session_path(subdomain: opts[:subdomain])
ActionController::RoutingError:
No route matches [GET] "/users/sign_in"
即使传入了子域,它也会被忽略,除非有子域,否则该路由不可用。
经过一些谷歌搜索并弄清楚如何为lvh.me设置config.allow_url部分之后,我还发现我应该将它添加到我的development.rb文件中,以便知道端口:
Capybara.always_include_port = true
这是有效的,因为上面的日志输出显示它正在使用端口3000。
接下来,我更改了sign_user_in方法,再次使用访问new_user_session_url(子域名:opts [:subdomain])(因此我之前的规范仍然会通过)。按照这个SO线程的建议:Capybara with subdomains - default_host我对我的规范进行了调整:
before(:each) do
set_host "lvh.me:3000"
end
def set_host (host)
default_url_options[:host] = host
Capybara.app_host = "http://" + host
end
现在,当我运行规范时,我在日志中看到以下内容:
Received "AllowUrl(*.lvh.me)"
Started "AllowUrl(*.lvh.me)"
Finished "AllowUrl(*.lvh.me)" with response "Success()"
Wrote response true ""
Received "Visit(http://subdomain1.lvh.me:3000/users/sign_in)"
Started "Visit(http://subdomain1.lvh.me:3000/users/sign_in)"
Load started
"Visit(http://subdomain1.lvh.me:3000/users/sign_in)" started page load
Started request to "http://subdomain1.lvh.me:3000/users/sign_in"
Finished "Visit(http://subdomain1.lvh.me:3000/users/sign_in)" with response "Success()"
Started request to "http://lvh.me:3000/"
Received 302 from "http://subdomain1.lvh.me:3000/users/sign_in"
Started request to "http://lvh.me:3000/assets/application.self-e7adbbd6d89b36b8d2524d4a3bbcb85ee152c7a2641271423c86da07df306565.css?body=1"
Started request to "http://lvh.me:3000/assets/jquery.self-660adc51e0224b731d29f575a6f1ec167ba08ad06ed5deca4f1e8654c135bf4c.js?body=1"
Started request to "http://lvh.me:3000/assets/bootstrap/transition.self-6ad2488465135ab731a045a8ebbe3ea2fc501aed286042496eda1664fdd07ba9.js?body=1"
更多进步!它现在包括子域,端口,并指向端口3000.我仍然得到相同的错误:
1) user creates product successfully
Failure/Error: fill_in 'user[email]', with: user.email
Capybara::ElementNotFound:
Unable to find field "user[email]"
该SO线程的评论之一是:
“这非常有效。另外,如果您使用的是像lvh.me这样的公共域,您可以使用Capybara.server_port = 31234自动设置端口,然后使用set_host”lvh.me:31234“
所以在我的rails_helper中,我在设置app_host:
的下方添加了server_portCapybara.app_host = 'http://lvh.me/'
Capybara.server_port = 31234
并将before(:each)
更改为使用端口31234.结果相同:
"Visit(http://subdomain6.lvh.me:31234/users/sign_in)" started page load
Started request to "http://subdomain6.lvh.me:31234/users/sign_in"
Finished "Visit(http://subdomain6.lvh.me:31234/users/sign_in)" with response "Success()"
Started request to "http://lvh.me:31234/"
Received 302 from "http://subdomain6.lvh.me:31234/users/sign_in"
Started request to "http://lvh.me:31234/assets/application-2f17abe5cd0f04e7f5455c4ae0a6e536b5d84dd05e600178874c6a5938ac0804.css"
Started request to "http://lvh.me:31234/assets/application-8e1c2330cf761b5bfefcaa648b8994224c7c6a87b2f76475831c76474ddca9d1.js"
Received 200 from "http://lvh.me:31234/"
Received 200 from "http://lvh.me:31234/assets/application-8e1c2330cf761b5bfefcaa648b8994224c7c6a87b2f76475831c76474ddca9d1.js"
Received 200 from "http://lvh.me:31234/assets/application-2f17abe5cd0f04e7f5455c4ae0a6e536b5d84dd05e600178874c6a5938ac0804.css"
...
Also see this repeated about 100 times (in this and in all previous examples):
Wrote response true ""
Received "FindXpath(.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')][(((./@id = 'user[email]' or ./@name = 'user[email]') or ./@placeholder = 'user[email]') or ./@id = //label[normalize-space(string(.)) = 'user[email]']/@for)] | .//label[normalize-space(string(.)) = 'user[email]']//.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')])"
Started "FindXpath(.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')][(((./@id = 'user[email]' or ./@name = 'user[email]') or ./@placeholder = 'user[email]') or ./@id = //label[normalize-space(string(.)) = 'user[email]']/@for)] | .//label[normalize-space(string(.)) = 'user[email]']//.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')])"
Finished "FindXpath(.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')][(((./@id = 'user[email]' or ./@name = 'user[email]') or ./@placeholder = 'user[email]') or ./@id = //label[normalize-space(string(.)) = 'user[email]']/@for)] | .//label[normalize-space(string(.)) = 'user[email]']//.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')])" with response "Success()"
Wrote response true ""
Received "FindXpath(.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')][(((./@id = 'user[email]' or ./@name = 'user[email]') or ./@placeholder = 'user[email]') or ./@id = //label[contains(normalize-space(string(.)), 'user[email]')]/@for)] | .//label[contains(normalize-space(string(.)), 'user[email]')]//.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')])"
Started "FindXpath(.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')][(((./@id = 'user[email]' or ./@name = 'user[email]') or ./@placeholder = 'user[email]') or ./@id = //label[contains(normalize-space(string(.)), 'user[email]')]/@for)] | .//label[contains(normalize-space(string(.)), 'user[email]')]//.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')])"
Finished "FindXpath(.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')][(((./@id = 'user[email]' or ./@name = 'user[email]') or ./@placeholder = 'user[email]') or ./@id = //label[contains(normalize-space(string(.)), 'user[email]')]/@for)] | .//label[contains(normalize-space(string(.)), 'user[email]')]//.//*[self::input | self::textarea][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'radio' or ./@type = 'checkbox' or ./@type = 'hidden' or ./@type = 'file')])" with response "Success()"
...
但是,唉,得到相同的结果:
Failures:
1) user creates product successfully
Failure/Error: fill_in 'user[email]', with: user.email
Capybara::ElementNotFound:
Unable to find field "user[email]"
它似乎应该有效,但必须有我遗漏的东西。任何帮助将不胜感激。
答案 0 :(得分:1)
从您的日志中看起来您的请求是" http://subdomain6.lvh.me:31234/users/sign_in"正在重定向到" http://lvh.me:31234/"如果帐户不存在会发生这种情况。我猜你没有禁用交易测试,这意味着应用程序实际上无法查看测试线程中创建的记录。请参阅 - https://github.com/jnicklas/capybara#transactions-and-database-setup和https://github.com/DatabaseCleaner/database_cleaner#rspec-with-capybara-example