Rspec 不可靠/片状水豚/AngularJS集成测试存在时间问题 如何使这些测试可靠地通过?
目前,这些测试是不可靠的。Rspec 不可靠/片状水豚/AngularJS集成测试存在时间问题 如何使这些测试可靠地通过?,rspec,phantomjs,capybara,integration-testing,poltergeist,Rspec,Phantomjs,Capybara,Integration Testing,Poltergeist,目前,这些测试是不可靠的。 有时他们会经过。有时他们会失败。 下面是演示此问题的设置、代码和输出。 建议克服这个问题将非常感谢,我相信将帮助许多其他人,所以请评论 测试代码环境 轨道3.2 rspec2.x 水豚 闹鬼 幻影 安格拉斯 谷歌浏览器47.0.2526.106版(64位) 从Gemfile.lock测试Gems 我尝试过的事情 确保我使用水豚的等待DSL匹配器 确保正确设置了“我的数据库清理器” 测试每个页面项,假设它可能不在页面上,并且仍在加载 缩小不一致测试的范围 单独运行不一致
有时他们会经过。有时他们会失败。
下面是演示此问题的设置、代码和输出。
建议克服这个问题将非常感谢,我相信将帮助许多其他人,所以请评论 测试代码环境
- i、 e.创建一个新记录,并假设页面已重定向,且记录位于页面上,单击“打开”
- 。单击不一致的“工作”
[2]
还有更多 测试是如何运行的
rspec spec/integration/costings/show_costing_spect.rb——格式文档
require "spec_helper"
RSpec.describe "Show a new costing in the listing," do
before :each do
admin_sign_in
create_costing("test1")
end
it "shows the costing after creation" do
within "#costings_table" do
expect(page).to have_css("#name", text: "test1")
end
end
it "shows the details of the new costing after creation" do
expect(page).to have_content("Costings")
within "#costings_table" do
expect(page).to have_content("test1")
all("#show").last.click
end
expect(page).to have_content("Costing Details")
expect(page).to have_css("#name", text: "test1")
end
end
测试代码
显示成本核算规格rb
require "spec_helper"
RSpec.describe "Show a new costing in the listing," do
before :each do
admin_sign_in
create_costing("test1")
end
it "shows the costing after creation" do
within "#costings_table" do
expect(page).to have_css("#name", text: "test1")
end
end
it "shows the details of the new costing after creation" do
expect(page).to have_content("Costings")
within "#costings_table" do
expect(page).to have_content("test1")
all("#show").last.click
end
expect(page).to have_content("Costing Details")
expect(page).to have_css("#name", text: "test1")
end
end
spec_helper.rb
# This file is copied to spec/ when you run 'rails generate r spec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
# Add library functions here so we can test them.
require File.expand_path(File.dirname(__FILE__) + "/../lib/general")
require 'rspec/rails'
require 'rspec/autorun'
# Integration Testing
require 'capybara/poltergeist'
Capybara.register_driver :poltergeist_debug do |app|
Capybara::Poltergeist::Driver.new(app, :inspector => true)
end
Capybara.javascript_driver = :poltergeist_debug
Capybara.default_driver = :poltergeist_debug
# Capybara Integration Test Helpers
def admin_sign_in
visit "/login"
#Create staff member in database
Staff.make!(:admin)
#Log In
fill_in "staff_username", with: "adminstaff"
fill_in "staff_password", with: "password"
click_button "login"
end
def create_costing(item)
visit "/api#/costings"
click_on "new_btn"
within "#form_costing" do
find("#name", match: :first).set("#{item}")
find("#description", match: :first).set("test description")
find("#from_date", match: :first).set("15/02/2016")
find("#cost_hourly_cents", match: :first).set("1.00")
click_on "create_btn"
end
end
RSpec.configure do |config|
config.before(:suite) do
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
require File.expand_path(File.dirname(__FILE__) + "/support/blueprints")
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
end
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# Allow a 'focus' tag so that we can run just a few tests which we are currently working on
config.treat_symbols_as_metadata_keys_with_true_values = true
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.filter_run_excluding :slow unless ENV["SLOW_SPECS"]
# Defer Garbage Collection
config.before(:all) { DeferredGarbageCollection.start }
config.after(:all) { DeferredGarbageCollection.reconsider }
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
# config.infer_spec_type_from_file_location!
# Configure Database Cleaner
config.include Capybara::DSL
config.before(:suite) do
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, :js => true) do
DatabaseCleaner.strategy = :truncation
end
config.before(:each) do
DatabaseCleaner.start
end
config.after(:each) do
DatabaseCleaner.clean
end
end
测试结果
测试运行1:失败
运行选项:
包括{:focus=>true}
排除{:slow=>true}
所有的例子都被过滤掉了;忽略{:focus=>true}
在列表中显示新的成本计算,
显示创建后的成本计算
显示创建后新成本计算的详细信息(失败-1)
失败:
1) 在列表中显示新成本,显示创建后新成本计算的详细信息
失败/错误:希望(第页)有内容(“test1”)
期望#has#u content?(“test1”)返回true,得到false
#./spec/integration/costings/show_costing_spec.rb:20:in block(3级)in
require "spec_helper"
RSpec.describe "Show a new costing in the listing," do
before :each do
admin_sign_in
create_costing("test1")
end
it "shows the costing after creation" do
within "#costings_table" do
expect(page).to have_css("#name", text: "test1")
end
end
it "shows the details of the new costing after creation" do
expect(page).to have_content("Costings")
within "#costings_table" do
expect(page).to have_content("test1")
all("#show").last.click
end
expect(page).to have_content("Costing Details")
expect(page).to have_css("#name", text: "test1")
end
end
#./spec/integration/costings/show_costing_spec.rb:19:in block(2级)in
require "spec_helper"
RSpec.describe "Show a new costing in the listing," do
before :each do
admin_sign_in
create_costing("test1")
end
it "shows the costing after creation" do
within "#costings_table" do
expect(page).to have_css("#name", text: "test1")
end
end
it "shows the details of the new costing after creation" do
expect(page).to have_content("Costings")
within "#costings_table" do
expect(page).to have_content("test1")
all("#show").last.click
end
expect(page).to have_content("Costing Details")
expect(page).to have_css("#name", text: "test1")
end
end
以5.46秒完成
2例,1例失败
测试运行2:通过
运行选项:
包括{:focus=>true}
排除{:slow=>true}
所有的例子都被过滤掉了;忽略{:focus=>true}
在列表中显示新成本,显示创建后的成本计算
显示创建后新成本计算的详细信息 以3.57秒完成 2个示例,0个失败 更新1 将测试gems升级到以下版本:
水豚(2.6.2)来自(2.1.0)
数据库\u从(0.7.1)中删除(1.5.1)
调试检查程序(0.0.2)
防护捆扎机(0.1.3)
卫兵利弗雷罗德(1.2.0)
防护规格(2.1.2)
茉莉花(0.0.10)
pg(0.17.1)
幻影JS(2.1.1.0)
从(1.4.1)到(1.9.0)
量角器导轨(0.0.17)
从(0.9.12)撬起(0.10.3)
机架(1.4.7)
机架测试(0.6.3)
轨道(3.2.21)
从(1.3.20)到(1.4.9)
rspec导轨(3.4.2)来自(2.11.4)
simplecov(0.8.2)
链轮(2.2.3)
宙斯(0.13.3)
zeus-parallel_试验(0.2.1)
结果1
不幸的是,升级这些宝石似乎并没有带来什么不同,我的测试仍然很糟糕
更新2
我执行了汤姆·沃尔波尔的建议。确保我的管理员登录等待登录完成
还按照Tom的建议更新了我的数据库设置
结果2
对我的堆栈来说,这些更改似乎没有效果
注意:如果不使用AngularJS,我觉得这些变化会有所不同。汤姆,谢谢你的建议
更新3
我需要获得更多关于测试运行期间发生的事情的信息。有人建议在网上登录,使用屏幕截图保存宝石等,但我不觉得这些将是最有效的时间。我想指定测试暂停的位置,并查看变量和表单字段的内容。在浏览器中是理想的
我用过的我使用“保存和打开页面”和“打印页面.html”进行调试 我搬到哪里去了
在运行RSpec 3.4.2时,我添加了一个调试助手方法: rails_helper.rb
def debugit
puts current_url
require 'pry'
binding.pry
end
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
# Add library functions here so we can test them.
require File.expand_path(File.dirname(__FILE__) + "/../lib/general")
require 'rspec/rails'
require 'devise'
RSpec.configure do |config|
config.before(:suite) do
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
require File.expand_path(File.dirname(__FILE__) + "/support/blueprints")
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
# Setup Devise before it is used in rails_helper
config.include Devise::TestHelpers, :type => :controller
Devise.stretches = 1 # Improves speed.
end
config.include Capybara::DSL, :type => :feature
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# Allow a 'focus' tag so that we can run just a few tests which we are currently working on
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.filter_run_excluding :slow unless ENV["SLOW_SPECS"]
# Defer Garbage Collection
config.before(:all) { DeferredGarbageCollection.start }
config.after(:all) { DeferredGarbageCollection.reconsider }
# Integration Testing
require 'capybara/rspec'
require 'capybara/poltergeist'
Capybara.register_driver :poltergeist_debug do |app|
Capybara::Poltergeist::Driver.new(app, {:inspector => true, js_errors: false })
end
Capybara.javascript_driver = :poltergeist_debug
Capybara.default_driver = :poltergeist_debug
# Debugging tools
def debugit
puts current_url
require 'pry'
binding.pry
end
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
#Show Deprications As Errors with full backtracing
config.raise_errors_for_deprecations!
#rest of the file....
# Final part of Configure Database Cleaner
Capybara.default_max_wait_time = 5
config.use_transactional_fixtures = false
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from
rails_helper.rb (or set it to false) to prevent uncommitted
transactions being used in JavaScript-dependent specs. During
testing, the app-under-test that the browser driver connects to
uses a different database connection to the database connection
used by the spec. The app's database connection would not be
able to access uncommitted transaction data setup over the
spec's database connection.
MSG
end
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so continue to use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
if !driver_shares_db_connection_with_specs
# Driver is probably for an external browser with an app
# under test that does *not* share a database connection with the
# specs, so use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end
end
def admin_sign_in
visit "/login"
#Create staff member in database
Staff.make!(:admin)
#Log In
fill_in "staff_username", with: "adminstaff"
fill_in "staff_password", with: "password"
click_button "login"
expect(page).to have_text('Logout')
end
def create_costing(item)
@item = item
visit "/api#/costings"
expect(page).to have_selector("#new_btn")
click_on "new_btn"
expect(page).to have_text("New Costing")
within "#form_costing" do
fill_in "name", with: "#{@item}"
fill_in "description", with: "test description"
fill_in "from_date1", with: "15/02/2015"
fill_in "cost_hourly_cents1", with: "12.00"
expect(page).to have_selector("#create_btn")
click_on "create_btn"
end
expect(page.find("#page_title")).to have_content("Costings")
end
结果3
控制台中将打印一个URL,测试将暂停。在这个阶段,我将能够导航到URL、登录、导航到测试页面并查看Capybara测试所做的工作
这使我能够确定当测试使用capybara的fill_in DSL时出现问题的根源。
在某些测试运行中,将正确填充字段并提交表单。
在另一种情况下,表单将正确填写,但“提交”按钮将被快速点击。这里的结果是创建了一条记录,但名称和描述的输入字段没有持久化
更新4
我发现因为我使用的是AngularJS输入表单和表,所以AngularJS需要一点时间来绑定到输入字段。如果这次不允许,则不会保存输入数据
<div ng-if="tableParams.data">
<table id="costings_table ng-table="tableParams" class="table">
<td id="field1">{{table.field1}}</td>
<td id="field2">{{table.field2}}</td>
</table>
</div>
require "rails_helper"
RSpec.describe "Show a new costing in the listing,", :type => :feature do
before :each do
admin_sign_in
create_costing("test1")
end
it "shows the costing after creation" do
within "#costings_table" do
expect(page.find("#code2")).to have_content("2")
expect(page.find("#name2")).to have_content("test1")
end
end
it "shows the details of the new costing after creation" do
within "#costings_table" do
click_on "show2"
end
expect(page.find("#page_title")).to have_content("Costing Details")
expect(page.find("#code")).to have_content("2")
expect(page.find("#name")).to have_content("test1")
expect(page.find("#description")).to have_content("test description")
end
end
# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
# Add library functions here so we can test them.
require File.expand_path(File.dirname(__FILE__) + "/../lib/general")
require 'rspec/rails'
require 'devise'
RSpec.configure do |config|
config.before(:suite) do
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
require File.expand_path(File.dirname(__FILE__) + "/support/blueprints")
Dir[Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
# Setup Devise before it is used in rails_helper
config.include Devise::TestHelpers, :type => :controller
Devise.stretches = 1 # Improves speed.
end
config.include Capybara::DSL, :type => :feature
config.mock_with :rspec
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
config.fixture_path = "#{::Rails.root}/spec/fixtures"
# Allow a 'focus' tag so that we can run just a few tests which we are currently working on
config.filter_run focus: true
config.run_all_when_everything_filtered = true
config.filter_run_excluding :slow unless ENV["SLOW_SPECS"]
# Defer Garbage Collection
config.before(:all) { DeferredGarbageCollection.start }
config.after(:all) { DeferredGarbageCollection.reconsider }
# Integration Testing
require 'capybara/rspec'
require 'capybara/poltergeist'
Capybara.register_driver :poltergeist_debug do |app|
Capybara::Poltergeist::Driver.new(app, {:inspector => true, js_errors: false })
end
Capybara.javascript_driver = :poltergeist_debug
Capybara.default_driver = :poltergeist_debug
# Debugging tools
def debugit
puts current_url
require 'pry'
binding.pry
end
# If you're not using ActiveRecord, or you'd prefer not to run each of your
# examples within a transaction, remove the following line or assign false
# instead of true.
config.use_transactional_fixtures = false
#Show Deprications As Errors with full backtracing
config.raise_errors_for_deprecations!
#rest of the file....
# Final part of Configure Database Cleaner
Capybara.default_max_wait_time = 5
config.use_transactional_fixtures = false
config.before(:suite) do
if config.use_transactional_fixtures?
raise(<<-MSG)
Delete line `config.use_transactional_fixtures = true` from
rails_helper.rb (or set it to false) to prevent uncommitted
transactions being used in JavaScript-dependent specs. During
testing, the app-under-test that the browser driver connects to
uses a different database connection to the database connection
used by the spec. The app's database connection would not be
able to access uncommitted transaction data setup over the
spec's database connection.
MSG
end
DatabaseCleaner.clean_with(:truncation)
end
config.before(:each) do
DatabaseCleaner.strategy = :transaction
end
config.before(:each, type: :feature) do
# :rack_test driver's Rack app under test shares database connection
# with the specs, so continue to use transaction strategy for speed.
driver_shares_db_connection_with_specs = Capybara.current_driver == :rack_test
if !driver_shares_db_connection_with_specs
# Driver is probably for an external browser with an app
# under test that does *not* share a database connection with the
# specs, so use truncation strategy.
DatabaseCleaner.strategy = :truncation
end
end
config.before(:each) do
DatabaseCleaner.start
end
config.append_after(:each) do
DatabaseCleaner.clean
end
end
def admin_sign_in
visit "/login"
#Create staff member in database
Staff.make!(:admin)
#Log In
fill_in "staff_username", with: "adminstaff"
fill_in "staff_password", with: "password"
click_button "login"
expect(page).to have_text('Logout')
end
def create_costing(item)
@item = item
visit "/api#/costings"
expect(page).to have_selector("#new_btn")
click_on "new_btn"
expect(page).to have_text("New Costing")
within "#form_costing" do
fill_in "name", with: "#{@item}"
fill_in "description", with: "test description"
fill_in "from_date1", with: "15/02/2015"
fill_in "cost_hourly_cents1", with: "12.00"
expect(page).to have_selector("#create_btn")
click_on "create_btn"
end
expect(page.find("#page_title")).to have_content("Costings")
end