多个 Angular 请求在测试中导致错误
Multiple Angular requests cause errors within tests
我有一个 Angular 1.5 应用程序,后端是 Rails 4.0同时。在测试中(使用 Capybara + selenium + chromeheadless)这会导致这些请求导致后端错误。在开发中它工作正常。
错误示例;
NoMethodError at /entities/get_current_entity
=============================================
> undefined method `fields' for nil:NilClass
app/controllers/application_controller.rb, line 79
--------------------------------------------------
``` ruby
74
75 private
76
77 def set_locale
78 #I18n.locale = params[:locale] if params[:locale].present?
> 79 if current_staff.nil? or current_staff.locale.nil?
80 I18n.locale = "en"
81 else
82 I18n.locale = current_staff.locale
83 end
84 end
```
App backtrace
-------------
- app/controllers/application_controller.rb:79:in `set_locale'
<Snip>
未通过的测试
require "rails_helper"
RSpec.describe "Login", :type => :feature do
describe "login with existing staff member" do
before do
create_admin
end
it "logs in as admin" do
visit "#/login"
fill_in "Email", with: "admin@test.com.au"
fill_in "Password", with: "password"
click_button "Login"
within "#staff-status" do
expect(page).to have_text('LOGOUT')
end
within("#flash-messages") do
expect(page).to have_text('Successful login')
end
# Check session variables
within("#selected-entity") do
expect(page).to have_text("LOGIN DEFAULT COMPANY")
end
within("#selected-ledger") do
expect(page).to have_text("My Ledger")
end
within("#selected-division") do
expect(page).to have_text("1-General")
end
end
end
end
这是导致挂起调用的代码
$scope.$on('devise:new-session', (event, currentStaff) ->
# staff logged in by Auth.login({...})
Auth.currentUser().then((staff) ->
FirmService.current_entity($rootScope)
DivisionService.current_division($rootScope)
LedgerService.current_ledger($rootScope)
$rootScope.current_staff = staff
, (error) ->
console.log("fail")
)
$location.path "/dashboard"
)
这在整个应用程序中都会发生,而在 Rails 3.2 上不会发生。只是因为我正在升级到 Rails 4 才开始发生这种情况。
如果我使用 promises 那么这个问题就不会发生。我不能总是使用 promises 来获取数据,这也会降低应用程序的速度。
这是登录的解决方法;
Auth.currentUser().then((staff) ->
FirmService.current_entity($rootScope).then ->
DivisionService.current_division($rootScope).then ->
LedgerService.current_ledger($rootScope)
如何让 Rails 4 在测试模式下处理这样的多个请求?
更新:运行服务器在测试环境
我 运行 我在测试环境中的服务器尝试手动复制问题,但这样做时它起作用了。所以一定和测试的速度或者水豚本身有关系吧?
更新:更新宝石
我更新到水豚 2.15.2、selenium-webdriver 3.6.0 和 chromedriver-helper 1.1.0。这没有帮助。
更新:错误
有时错误会改变,即使用户已成功登录,我的一个请求也会出现错误 'Staff not logged in'。
更新:调试
将 'require "pry";binding.pry' 插入到 Rails 控制器中,AngularJS 调用最终无法按预期工作。我在 AngularJS 代码中有三个调用,我希望 Rails 会命中调试行 3 次。第一次我 运行 它只命中其中一条调试线,第二次它没有命中任何调试线。相反,我在 /entities/get_current_entity 处得到了 NoMethodError 并且对于部门和分类帐也是如此。
更新:调查撬挂
如果我将 binding.pry 放入 set_locale 方法并调用 current_staff.locale.nil?我发现有时 pry 会 hang 永远在那里。
注释掉 set_locale 内容后,它继续运行但仍然失败,在 test.log 文件中我得到这些错误;
Completed 500 Internal Server Error in 4690ms
Completed 500 Internal Server Error in 4688ms
Completed 500 Internal Server Error in 4686ms
NoMethodError - undefined method `fields' for nil:NilClass:
<snip>
NoMethodError - undefined method `fields' for nil:NilClass:
<snip>
NoMethodError - undefined method `collect' for nil:NilClass:
<snip>
(1.3ms) ALTER TABLE "indirect_taxes" DISABLE TRIGGER ALL;ALTER TABLE "ledger_defaults" DISABLE TRIGGER ALL;ALTER TABLE "ledgers" DISABLE TRIGGER ALL;ALTER TABLE "levels" DISABLE TRIGGER ALL;ALTER TABLE "locations" DISABLE TRIGGER ALL;ALTER TABLE "phones" DISABLE TRIGGER ALL;ALTER TABLE "sessions" DISABLE TRIGGER ALL;ALTER TABLE "transactions" DISABLE TRIGGER ALL;ALTER TABLE "versions" DISABLE TRIGGER ALL;ALTER TABLE "schema_migrations" DISABLE TRIGGER ALL;ALTER TABLE "staff" DISABLE TRIGGER ALL;ALTER TABLE "tickets" DISABLE TRIGGER ALL;ALTER TABLE "timesheets" DISABLE TRIGGER ALL;ALTER TABLE "websites" DISABLE TRIGGER ALL;ALTER TABLE "customers" DISABLE TRIGGER ALL;ALTER TABLE "costings" DISABLE TRIGGER ALL;ALTER TABLE "disbursements" DISABLE TRIGGER ALL;ALTER TABLE "employees" DISABLE TRIGGER ALL;ALTER TABLE "entities" DISABLE TRIGGER ALL;ALTER TABLE "folios" DISABLE TRIGGER ALL;ALTER TABLE "work_descs" DISABLE TRIGGER ALL;ALTER TABLE "account_groups" DISABLE TRIGGER ALL;ALTER TABLE "accounts" DISABLE TRIGGER ALL;ALTER TABLE "addresses" DISABLE TRIGGER ALL;ALTER TABLE "business_types" DISABLE TRIGGER ALL;ALTER TABLE "charges" DISABLE TRIGGER ALL;ALTER TABLE "comments" DISABLE TRIGGER ALL;ALTER TABLE "invoices" DISABLE TRIGGER ALL;ALTER TABLE "jobs" DISABLE TRIGGER ALL;ALTER TABLE "divisions" DISABLE TRIGGER ALL;ALTER TABLE "emails" DISABLE TRIGGER ALL
(411.5ms) TRUNCATE TABLE "public"."indirect_taxes", "public"."ledger_defaults", "public"."ledgers", "public"."levels", "public"."locations", "public"."phones", "public"."sessions", "public"."transactions", "public"."versions", "public"."staff", "public"."tickets", "public"."timesheets", "public"."websites", "public"."customers", "public"."costings", "public"."disbursements", "public"."employees", "public"."entities", "public"."folios", "public"."work_descs", "public"."account_groups", "public"."accounts", "public"."addresses", "public"."business_types", "public"."charges", "public"."comments", "public"."invoices", "public"."jobs", "public"."divisions", "public"."emails" RESTART IDENTITY CASCADE;
(1.9ms) ALTER TABLE "indirect_taxes" ENABLE TRIGGER ALL;ALTER TABLE "ledger_defaults" ENABLE TRIGGER ALL;ALTER TABLE "ledgers" ENABLE TRIGGER ALL;ALTER TABLE "levels" ENABLE TRIGGER ALL;ALTER TABLE "locations" ENABLE TRIGGER ALL;ALTER TABLE "phones" ENABLE TRIGGER ALL;ALTER TABLE "sessions" ENABLE TRIGGER ALL;ALTER TABLE "transactions" ENABLE TRIGGER ALL;ALTER TABLE "versions" ENABLE TRIGGER ALL;ALTER TABLE "staff" ENABLE TRIGGER ALL;ALTER TABLE "tickets" ENABLE TRIGGER ALL;ALTER TABLE "timesheets" ENABLE TRIGGER ALL;ALTER TABLE "schema_migrations" ENABLE TRIGGER ALL;ALTER TABLE "websites" ENABLE TRIGGER ALL;ALTER TABLE "customers" ENABLE TRIGGER ALL;ALTER TABLE "costings" ENABLE TRIGGER ALL;ALTER TABLE "disbursements" ENABLE TRIGGER ALL;ALTER TABLE "employees" ENABLE TRIGGER ALL;ALTER TABLE "entities" ENABLE TRIGGER ALL;ALTER TABLE "folios" ENABLE TRIGGER ALL;ALTER TABLE "work_descs" ENABLE TRIGGER ALL;ALTER TABLE "account_groups" ENABLE TRIGGER ALL;ALTER TABLE "accounts" ENABLE TRIGGER ALL;ALTER TABLE "addresses" ENABLE TRIGGER ALL;ALTER TABLE "business_types" ENABLE TRIGGER ALL;ALTER TABLE "charges" ENABLE TRIGGER ALL;ALTER TABLE "comments" ENABLE TRIGGER ALL;ALTER TABLE "invoices" ENABLE TRIGGER ALL;ALTER TABLE "jobs" ENABLE TRIGGER ALL;ALTER TABLE "divisions" ENABLE TRIGGER ALL;ALTER TABLE "emails" ENABLE TRIGGER ALL
我认为 Truncate table 的最后一点是试图清理混乱的数据库清理器。所以我得到的那三个服务器错误似乎超时了。为什么数据库 / Rails 会停止监听查询?
当我输入一个简单的查询时,pry 也会挂在 set_locale 方法中
Staff.all
更新:strace 输出
我已经获取了 pry 挂起时的 strace 输出;
https://gist.github.com/map7/f25f75457f1a6ae6995934e2c2744660
经过一番交谈,发现使用了一个在多个线程之间共享数据库连接的补丁。问题是,这可能会导致多个同时请求的各种问题,并且通常会导致测试中的潜在漏洞。删除补丁修复了测试。
一旦您一直迁移到 Rails 5.1,然后 Rails 会通过互斥体处理测试环境中的连接共享,允许事务性功能测试,并避免需要database_cleaner
我有一个 Angular 1.5 应用程序,后端是 Rails 4.0同时。在测试中(使用 Capybara + selenium + chromeheadless)这会导致这些请求导致后端错误。在开发中它工作正常。
错误示例;
NoMethodError at /entities/get_current_entity
=============================================
> undefined method `fields' for nil:NilClass
app/controllers/application_controller.rb, line 79
--------------------------------------------------
``` ruby
74
75 private
76
77 def set_locale
78 #I18n.locale = params[:locale] if params[:locale].present?
> 79 if current_staff.nil? or current_staff.locale.nil?
80 I18n.locale = "en"
81 else
82 I18n.locale = current_staff.locale
83 end
84 end
```
App backtrace
-------------
- app/controllers/application_controller.rb:79:in `set_locale'
<Snip>
未通过的测试
require "rails_helper"
RSpec.describe "Login", :type => :feature do
describe "login with existing staff member" do
before do
create_admin
end
it "logs in as admin" do
visit "#/login"
fill_in "Email", with: "admin@test.com.au"
fill_in "Password", with: "password"
click_button "Login"
within "#staff-status" do
expect(page).to have_text('LOGOUT')
end
within("#flash-messages") do
expect(page).to have_text('Successful login')
end
# Check session variables
within("#selected-entity") do
expect(page).to have_text("LOGIN DEFAULT COMPANY")
end
within("#selected-ledger") do
expect(page).to have_text("My Ledger")
end
within("#selected-division") do
expect(page).to have_text("1-General")
end
end
end
end
这是导致挂起调用的代码
$scope.$on('devise:new-session', (event, currentStaff) ->
# staff logged in by Auth.login({...})
Auth.currentUser().then((staff) ->
FirmService.current_entity($rootScope)
DivisionService.current_division($rootScope)
LedgerService.current_ledger($rootScope)
$rootScope.current_staff = staff
, (error) ->
console.log("fail")
)
$location.path "/dashboard"
)
这在整个应用程序中都会发生,而在 Rails 3.2 上不会发生。只是因为我正在升级到 Rails 4 才开始发生这种情况。
如果我使用 promises 那么这个问题就不会发生。我不能总是使用 promises 来获取数据,这也会降低应用程序的速度。
这是登录的解决方法;
Auth.currentUser().then((staff) ->
FirmService.current_entity($rootScope).then ->
DivisionService.current_division($rootScope).then ->
LedgerService.current_ledger($rootScope)
如何让 Rails 4 在测试模式下处理这样的多个请求?
更新:运行服务器在测试环境
我 运行 我在测试环境中的服务器尝试手动复制问题,但这样做时它起作用了。所以一定和测试的速度或者水豚本身有关系吧?
更新:更新宝石
我更新到水豚 2.15.2、selenium-webdriver 3.6.0 和 chromedriver-helper 1.1.0。这没有帮助。
更新:错误
有时错误会改变,即使用户已成功登录,我的一个请求也会出现错误 'Staff not logged in'。
更新:调试
将 'require "pry";binding.pry' 插入到 Rails 控制器中,AngularJS 调用最终无法按预期工作。我在 AngularJS 代码中有三个调用,我希望 Rails 会命中调试行 3 次。第一次我 运行 它只命中其中一条调试线,第二次它没有命中任何调试线。相反,我在 /entities/get_current_entity 处得到了 NoMethodError 并且对于部门和分类帐也是如此。
更新:调查撬挂
如果我将 binding.pry 放入 set_locale 方法并调用 current_staff.locale.nil?我发现有时 pry 会 hang 永远在那里。
注释掉 set_locale 内容后,它继续运行但仍然失败,在 test.log 文件中我得到这些错误;
Completed 500 Internal Server Error in 4690ms
Completed 500 Internal Server Error in 4688ms
Completed 500 Internal Server Error in 4686ms
NoMethodError - undefined method `fields' for nil:NilClass:
<snip>
NoMethodError - undefined method `fields' for nil:NilClass:
<snip>
NoMethodError - undefined method `collect' for nil:NilClass:
<snip>
(1.3ms) ALTER TABLE "indirect_taxes" DISABLE TRIGGER ALL;ALTER TABLE "ledger_defaults" DISABLE TRIGGER ALL;ALTER TABLE "ledgers" DISABLE TRIGGER ALL;ALTER TABLE "levels" DISABLE TRIGGER ALL;ALTER TABLE "locations" DISABLE TRIGGER ALL;ALTER TABLE "phones" DISABLE TRIGGER ALL;ALTER TABLE "sessions" DISABLE TRIGGER ALL;ALTER TABLE "transactions" DISABLE TRIGGER ALL;ALTER TABLE "versions" DISABLE TRIGGER ALL;ALTER TABLE "schema_migrations" DISABLE TRIGGER ALL;ALTER TABLE "staff" DISABLE TRIGGER ALL;ALTER TABLE "tickets" DISABLE TRIGGER ALL;ALTER TABLE "timesheets" DISABLE TRIGGER ALL;ALTER TABLE "websites" DISABLE TRIGGER ALL;ALTER TABLE "customers" DISABLE TRIGGER ALL;ALTER TABLE "costings" DISABLE TRIGGER ALL;ALTER TABLE "disbursements" DISABLE TRIGGER ALL;ALTER TABLE "employees" DISABLE TRIGGER ALL;ALTER TABLE "entities" DISABLE TRIGGER ALL;ALTER TABLE "folios" DISABLE TRIGGER ALL;ALTER TABLE "work_descs" DISABLE TRIGGER ALL;ALTER TABLE "account_groups" DISABLE TRIGGER ALL;ALTER TABLE "accounts" DISABLE TRIGGER ALL;ALTER TABLE "addresses" DISABLE TRIGGER ALL;ALTER TABLE "business_types" DISABLE TRIGGER ALL;ALTER TABLE "charges" DISABLE TRIGGER ALL;ALTER TABLE "comments" DISABLE TRIGGER ALL;ALTER TABLE "invoices" DISABLE TRIGGER ALL;ALTER TABLE "jobs" DISABLE TRIGGER ALL;ALTER TABLE "divisions" DISABLE TRIGGER ALL;ALTER TABLE "emails" DISABLE TRIGGER ALL
(411.5ms) TRUNCATE TABLE "public"."indirect_taxes", "public"."ledger_defaults", "public"."ledgers", "public"."levels", "public"."locations", "public"."phones", "public"."sessions", "public"."transactions", "public"."versions", "public"."staff", "public"."tickets", "public"."timesheets", "public"."websites", "public"."customers", "public"."costings", "public"."disbursements", "public"."employees", "public"."entities", "public"."folios", "public"."work_descs", "public"."account_groups", "public"."accounts", "public"."addresses", "public"."business_types", "public"."charges", "public"."comments", "public"."invoices", "public"."jobs", "public"."divisions", "public"."emails" RESTART IDENTITY CASCADE;
(1.9ms) ALTER TABLE "indirect_taxes" ENABLE TRIGGER ALL;ALTER TABLE "ledger_defaults" ENABLE TRIGGER ALL;ALTER TABLE "ledgers" ENABLE TRIGGER ALL;ALTER TABLE "levels" ENABLE TRIGGER ALL;ALTER TABLE "locations" ENABLE TRIGGER ALL;ALTER TABLE "phones" ENABLE TRIGGER ALL;ALTER TABLE "sessions" ENABLE TRIGGER ALL;ALTER TABLE "transactions" ENABLE TRIGGER ALL;ALTER TABLE "versions" ENABLE TRIGGER ALL;ALTER TABLE "staff" ENABLE TRIGGER ALL;ALTER TABLE "tickets" ENABLE TRIGGER ALL;ALTER TABLE "timesheets" ENABLE TRIGGER ALL;ALTER TABLE "schema_migrations" ENABLE TRIGGER ALL;ALTER TABLE "websites" ENABLE TRIGGER ALL;ALTER TABLE "customers" ENABLE TRIGGER ALL;ALTER TABLE "costings" ENABLE TRIGGER ALL;ALTER TABLE "disbursements" ENABLE TRIGGER ALL;ALTER TABLE "employees" ENABLE TRIGGER ALL;ALTER TABLE "entities" ENABLE TRIGGER ALL;ALTER TABLE "folios" ENABLE TRIGGER ALL;ALTER TABLE "work_descs" ENABLE TRIGGER ALL;ALTER TABLE "account_groups" ENABLE TRIGGER ALL;ALTER TABLE "accounts" ENABLE TRIGGER ALL;ALTER TABLE "addresses" ENABLE TRIGGER ALL;ALTER TABLE "business_types" ENABLE TRIGGER ALL;ALTER TABLE "charges" ENABLE TRIGGER ALL;ALTER TABLE "comments" ENABLE TRIGGER ALL;ALTER TABLE "invoices" ENABLE TRIGGER ALL;ALTER TABLE "jobs" ENABLE TRIGGER ALL;ALTER TABLE "divisions" ENABLE TRIGGER ALL;ALTER TABLE "emails" ENABLE TRIGGER ALL
我认为 Truncate table 的最后一点是试图清理混乱的数据库清理器。所以我得到的那三个服务器错误似乎超时了。为什么数据库 / Rails 会停止监听查询?
当我输入一个简单的查询时,pry 也会挂在 set_locale 方法中
Staff.all
更新:strace 输出
我已经获取了 pry 挂起时的 strace 输出; https://gist.github.com/map7/f25f75457f1a6ae6995934e2c2744660
经过一番交谈,发现使用了一个在多个线程之间共享数据库连接的补丁。问题是,这可能会导致多个同时请求的各种问题,并且通常会导致测试中的潜在漏洞。删除补丁修复了测试。
一旦您一直迁移到 Rails 5.1,然后 Rails 会通过互斥体处理测试环境中的连接共享,允许事务性功能测试,并避免需要database_cleaner