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匹配器 确保正确设置了“我的数据库清理器” 测试每个页面项,假设它可能不在页面上,并且仍在加载 缩小不一致测试的范围 单独运行不一致

目前,这些测试是不可靠的。
有时他们会经过。有时他们会失败。
下面是演示此问题的设置、代码和输出。
建议克服这个问题将非常感谢,我相信将帮助许多其他人,所以请评论

测试代码环境
  • 轨道3.2
  • rspec2.x
  • 水豚
  • 闹鬼
  • 幻影
  • 安格拉斯
  • 谷歌浏览器47.0.2526.106版(64位)
  • 从Gemfile.lock测试Gems 我尝试过的事情
  • 确保我使用水豚的等待DSL匹配器
  • 确保正确设置了“我的数据库清理器”
  • 测试每个页面项,假设它可能不在页面上,并且仍在加载
  • 缩小不一致测试的范围
  • 单独运行不一致的测试
  • 识别触发不一致测试结果的代码Capybara DSL

    • i、 e.创建一个新记录,并假设页面已重定向,且记录位于页面上,单击“打开”

    • 。单击不一致的“工作”
  • 将Capybara升级至最新版本(在单独的分支中)
  • 将Poltergeist和RSpec升级至最新版本(在另一个分支中,仍在进行此工作)
  • 我使用的资源 [1]
    [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