Pruebas de integración no confiables / Flakey Capybara / AngularJS con problemas de sincronización

¿Cómo hago para que estas pruebas pasen de manera confiable?

Actualmente estas pruebas son flakey.
A veces pasan. A veces fallan.
A continuación se muestra la configuración, el código y la salida que demuestran este problema.
Las sugerencias para superar este problema serán muy apreciadas y estoy seguro de que ayudarán a muchos otros, ¡así que por favor comente!

Entorno de código de pruebaCarriles 3.2RSpec 2.xCarpinchoDuendePhantomJSAngularJSGoogle Chrome versión 47.0.2526.106 (64 bits)Prueba de gemas desde Gemfile.lock
capybara (2.1.0)
database_cleaner (0.7.1)
debug_inspector (0.0.2)
guard-bundler (0.1.3)
guard-livereload (1.2.0)
guard-rspec (2.1.2)
jasminerice (0.0.10)
pg (0.17.1)
phantomjs (2.1.1.0)
poltergeist (1.4.1)
protractor-rails (0.0.17)
pry (0.9.12)
rack (1.4.7)
rack-test (0.6.3)
rails (3.2.21)
rails-assets-angular (1.3.20)
rspec-rails (2.11.4)
simplecov (0.8.2)
sprockets (2.2.3)
zeus (0.13.3)
zeus-parallel_tests (0.2.1)
Cosas que he intentadoAsegúrate de usar los marcadores DSL de espera de CapybaraAsegurarme de que mi limpiador de base de datos esté configurado correctamentePruebe todos los elementos de la página suponiendo que es posible que no esté en la página y que aún se esté cargandoLimite las pruebas inconsistentesEjecute una prueba inconsistente solo

Identifique los DSL de Capybara de código que desencadenan resultados de prueba inconsistentes.

es decir, crear un nuevo registro y asumir que la página se ha redirigido y que el registro está en la página click_on

o

.click no consistentemente 'trabajando'Actualice Capybara a la última versión (en una rama separada)Poltergeist y RSpec actualizados a la última versión (en una rama separada, aún trabajando en esto)Recursos que utilicé

[1]Carpincho El DSL
[2]Carpincho, PhantomJs, Poltergeist y Rspec Tips
Y muchos más...

Cómo se realizaron las pruebas

rspec spec/integration/costings/show_costing_spec.rb --format documentation

Código de pruebashow_costing_spec.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
Resultados de la prueba

Test Run 1: Failing

Opciones de ejecución: incluir {: focus => true} excluir {: slow => true}

Todos los ejemplos fueron filtrados; ignorando {: focus => true}

Mostrar un nuevo costo en la lista, muestra el costo después de la creación muestra los detalles del nuevo costo después de la creación (FALLIDO - 1)

Fallas:

1) Mostrar un nuevo costo en la lista,
muestra los detalles del nuevo costeo después de la creación
Falla / Error: esperar (página) .to tener_contenido ("prueba1")
esperaba que #has_content? ("test1") devuelva verdadero, obtuvo falso
# ./spec/integration/costings/show_costing_spec.rb:20:en bloque (3 niveles) en
# ./spec/integration/costings/show_costing_spec.rb:19:en bloque (2 niveles) en

Terminado en 5,46 segundos 2 ejemplos, 1 falla

Test Run 2: Passing

Opciones de ejecución: incluir {: focus => true} excluir {: slow => true}

Todos los ejemplos fueron filtrados; ignorando {: focus => true}

Mostrar un nuevo costo en la lista,
muestra el costo después de la creación
muestra los detalles del nuevo costeo después de la creación

Terminado en 3.57 segundos 2 ejemplos, 0 fallas

Actualización 1

Gemas de prueba actualizadas a las siguientes versiones:
capibara (2.6.2) de (2.1.0)
database_cleaner (1.5.1) de (0.7.1)
debug_inspector (0.0.2)
paquete de protección (0.1.3)
guardia-livereload (1.2.0)
especificación de guardia (2.1.2)
jazmín (0.0.10)
pág (0.17.1)
phantomjs (2.1.1.0)
poltergeist (1.9.0) de (1.4.1)
Carriles prolongadores (0.0.17)
palanca (0.10.3) de (0.9.12)
estante (1.4.7)
prueba de rack (0.6.3)
rieles (3.2.21)
rails-assets-angular (1.4.9) de (1.3.20)
rspec-rails (3.4.2) de (2.11.4)
simplecov (0.8.2)
piñones (2.2.3)
zeus (0.13.3)
zeus-parallel_tests (0.2.1)

Result 1

Desafortunadamente, la actualización de estas gemas no parecía hacer una diferencia y mis pruebas aún eran inestables.

Actualización 2

Implementé las sugerencias de Tom Walpole. Se aseguró de que mi admin_sign_in espere a que se complete sign_in.

También actualicé mi configuración de database_cleaner como sugirió Tom.

Result 2

Para mi pila, estos cambios no parecieron tener efecto.

Nota: Si uno no está usando AngularJS, creo que estos cambios habrían marcado la diferencia. Así que gracias Tom por tus sugerencias.

Actualización 3

Necesitaba obtener más información sobre lo que estaba sucediendo durante mis ejecuciones de prueba. Hay sugerencias en la red para iniciar sesión, usar gemas para guardar capturas de pantalla y similares, pero no sentí que fueran las más eficientes en el tiempo. Quería especificar dónde quería que la prueba se detuviera y ver el contenido de las variables y los campos de formulario. En un navegador sería lo ideal.

Lo que usé
Estaba usando "save_and_open_page" e "print page.html" para depurar.

A lo que me mudé
Mientras ejecutaba RSpec 3.4.2 agregué un método auxiliar de depuración:

rails_helper.rb

def debugit
  puts current_url
  require 'pry'
  binding.pry
end

Result 3

Se imprimirá una URL en la consola y la prueba se detendrá. En esta etapa, podría navegar a la URL, iniciar sesión, navegar a la página de prueba y ver lo que había hecho la prueba de Carpincho.

Esto me permitió identificar que la fuente de mis problemas surgió cuando la prueba estaba usando el relleno de carpincho en DSL. En algunas ejecuciones de prueba, los campos se completarán correctamente y se enviará el formulario. En el otro escenario, el formulario se completará correctamente, pero el botón de envío se presionará demasiado rápido. El resultado aquí es que se creó un registro pero no se persistieron los campos de entrada de nombre y descripción.

Actualización 4

Descubrí que debido a que estaba usando formularios y tablas de entrada de AngularJS, AngularJS requirió un poco de tiempo para enlazar los campos de entrada. Si no se permitiera esta vez, los datos de entrada no se guardarían.

Carpincho proporciona métodos de espera como "dentro" y "buscar". Los utilicé pero no me ayudaron con el problema del tiempo de enlace de AngularJS. Encontré que ng-if podría usarse para crear una declaración if para esperar un elemento en particular que significaría los enlaces de AngularJS a los campos de formulario completos.

Entonces utilicé los métodos de espera de Capybara para esperar los campos que quería llenar y usé AngularJS 'ng-if no para mostrar los campos hasta que estén listos.

Implementación
index.html.erb

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

Result 4

¡Las pruebas finalmente pasan! Sin embargo, tengo todos estos métodos de búsqueda con xpath para garantizar que se esperen elementos específicos y difíciles de apuntar ...

Actualización 5

Aunque en mi gemfile estaba ejecutando la gema phantomJS versión 2.1.1, mi versión de línea de comandos era solo 1.X. Esto resultó ser significativo.

Actualicé mi versión de línea de comando phantomJS a 2.1.1. Al mismo tiempo, me aseguré de que todos mis cuadros de entrada, botones, tablas y encabezados tuvieran identificadores únicos. Luego pude eliminar todas las ocurrencias de find (: xpath) sin romper las pruebas.

Result 5

¡Este conjunto de pruebas ahora pasa de manera confiable todo el tiempo! ¡Exactamente lo que quería! ¡Si!

Respuestas a la pregunta(2)

Su respuesta a la pregunta