Pruebas de integración no confiables / Flakey Capybara / AngularJS con problemas de sincronización
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!
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 soloIdentifique 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_ono
.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...
rspec spec/integration/costings/show_costing_spec.rb --format documentation
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 pruebaTest 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 1Gemas 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 2Implementé 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 3Necesitaba 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 4Descubrí 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 5Aunque 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!