Unzuverlässige / Flakey Capybara / AngularJS-Integrationstests mit Zeitproblemen
Derzeit sind diese Tests Flakey.
anchmal gehen sie vorbei. Manchmal scheitern sie.
Below ist das Setup, der Code und die Ausgabe, die dieses Problem demonstrieren.
Vorschläge zur Behebung dieses Problems werden sehr geschätzt, und ich bin mir sicher, dass ich vielen anderen helfen kann. Bitte kommentieren Sie dies!
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)
Things habe ich versuchttellen Sie sicher, dass ich Capybaras wartende DSL-Matcher verwendStellen Sie sicher, dass mein Datenbankbereiniger korrekt eingerichtet istTesten Sie jedes Seitenelement, vorausgesetzt, es befindet sich möglicherweise nicht auf der Seite und wird noch geladen.Inkonsistente Tests eingrenzen Inkonsistenten Test alleine ausführenIdentifizieren Sie Code-Capybara-DSLs, die Auslöser für inkonsistente Testergebnisse sind.
d.h. Erstellen eines neuen Datensatzes und Annahme, dass die Seite umgeleitet wurde und sich der Datensatz auf der Seite befindet click_onode
.click funktioniert nicht durchgehendUpgrade Capybara auf die neueste Version (in einem separaten Zweig)Upgrade von Poltergeist und RSpec auf die neueste Version (in einem separaten Zweig, der noch daran arbeitet)Ressourcen, die ich verwendet habe [1]Capybara Die DSL
[2]Capybara, PhantomJs, Poltergeist und Rspec Tips
Und viele mehr..
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
TestergebnissTest Run 1: Failing
Run-Optionen: {: focus => true} einschließen {: slow => true} ausschließen
Alle Beispiele wurden herausgefiltert; ignoriert {: focus => true}
Eine neue Kalkulation in der Auflistung anzeigen, zeigt die Kalkulation nach dem Anlegen an, zeigt die Details der neuen Kalkulation nach dem Anlegen an (FAILED - 1)
Failures:
1) Zeige eine neue Kalkulation in der Auflistung,
zeigt die Details der neuen Kalkulation nach dem Anlegen an
Fehler / Fehler: Erwarte (Seite) .to have_content ("test1")
expected #has_content? ("test1") um true zurückzugeben, got false
# ./spec/integration/costings/show_costing_spec.rb:20:in Block (3 Ebenen) in
# ./spec/integration/costings/show_costing_spec.rb:19:in Block (2 Ebenen) in
Finished in 5.46 Sekunden 2 Beispiele, 1 Fehler
Test Run 2: Passing
Run-Optionen: {: focus => true} einschließen {: slow => true} ausschließen
Alle Beispiele wurden herausgefiltert; ignoriert {: focus => true}
Eine neue Kalkulation in der Auflistung anzeigen,
zeigt die Kosten nach der Erstellung
zeigt die Details der neuen Kalkulation nach dem Anlegen an
In 3,57 Sekunden fertig 2 Beispiele, 0 Fehler
Update 1 Test-Edelsteine wurden auf die folgenden Versionen aktualisiert:
capybara (2.6.2) von (2.1.0)
database_cleaner (1.5.1) von (0.7.1)
debug_inspector (0.0.2)
guard-bundler (0.1.3)
guard-livereload (1.2.0)
guard-spec (2.1.2)
jasminerice (0.0.10)
pg (0.17.1)
phantomjs (2.1.1.0)
poltergeist (1.9.0) von (1.4.1)
Winkelmesserschienen (0.0.17)
pry (0.10.3) from (0.9.12)
rack (1.4.7)
rack-test (0.6.3)
rails (3.2.21)
Rails-Assets-Angular (1.4.9) von (1.3.20)
rspec-Rails (3.4.2) von (2.11.4)
simplecov (0.8.2)
sprockets (2.2.3)
zeus (0.13.3)
zeus-parallel_tests (0.2.1)
Result 1
eider schien die Aufrüstung dieser Edelsteine keinen Unterschied zu bewirken, und meine Tests waren immer noch unauffälli
Update 2Ich habe die Vorschläge von Tom Walpole umgesetzt. Es wurde sichergestellt, dass mein admin_sign_in auf den Abschluss von sign_in wartet.
Auch mein database_cleaner-Setup wurde aktualisiert, wie von Tom vorgeschlagen.
Result 2
Für meinen Stack schienen diese Änderungen keine Auswirkungen zu haben.
Hinweis Wenn man AngularJS nicht benutzt, haben diese Änderungen meiner Meinung nach einen Unterschied gemacht. Also danke Tom für deine Vorschläge.
Update 3Ich musste mehr Informationen darüber erhalten, was während meiner Testläufe passierte. Es gibt Vorschläge im Internet, wie man sich anmeldet, Edelsteine zum Speichern von Screenshots verwendet und dergleichen, aber ich hatte nicht das Gefühl, dass diese die effizienteste Zeit sind. Ich wollte angeben, wo der Test anhalten und den Inhalt von Variablen und Formularfeldern anzeigen soll. In einem Browser wäre das ideal.
Was ich benutzt habe
Ich habe "save_and_open_page" und "print page.html" zum Debuggen verwendet.
Was ich umgezogen bin
Als ich RSpec 3.4.2 ausführte, fügte ich eine Debug-Hilfsmethode hinzu:
rails_helper.rb
def debugit
puts current_url
require 'pry'
binding.pry
end
Result 3
Eine URL würde in der Konsole gedruckt und der Test unterbrochen. Zu diesem Zeitpunkt könnte ich zur URL navigieren, mich anmelden, zur Testseite navigieren und anzeigen, was der Capybara-Test getan hat.
Auf diese Weise konnte ich feststellen, dass die Ursache meiner Probleme bei der Verwendung von capybaras fill_in DSL lag. In einigen Testläufen wurden die Felder korrekt ausgefüllt und das Formular gesendet. In dem anderen Szenario würde das Formular korrekt ausgefüllt, aber die Senden-Schaltfläche würde zu schnell gedrückt. Dies hat zur Folge, dass ein Datensatz erstellt wurde, die Eingabefelder Name und Beschreibung jedoch nicht beibehalten wurden.
Update 4Ich habe festgestellt, dass AngularJS aufgrund der Verwendung von AngularJS-Eingabeformularen und -Tabellen nur wenig Zeit für die Bindung an die Eingabefelder benötigt. Wenn diesmal nicht erlaubt, werden die Eingabedaten nicht gespeichert.
Capybara bietet Wartemethoden wie "inside" und "find". Ich habe diese verwendet, aber sie haben bei der Ausgabe der AngularJS-Bindungszeit nicht geholfen. Ich fand, dass ng-if verwendet werden könnte, um eine if-Anweisung zu erstellen, die auf ein bestimmtes Element wartet, das angibt, dass die AngularJS-Bindungen zu den Formularfeldern vollständig sind.
So habe ich Capybara-Wartemethoden verwendet, um auf die Felder zu warten, die ich ausfüllen wollte, und ich habe AngularJS 'ng-if verwendet, um die Felder nicht anzuzeigen, bis sie fertig sind.
Implementierun
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
Tests endlich bestanden! Ich habe jedoch alle diese Find-Methoden mit xpath, die sicherstellen, dass auf bestimmte und schwer zu zielende Elemente gewartet wird ...
Update 5Obwohl ich in meiner Gemdatei die Gem-PhantomJS-Version 2.1.1 ausgeführt habe, war meine Befehlszeilenversion nur 1.X. Dies hat sich als bedeutsam erwiesen.
Ich habe meine Kommandozeilen-PhantomJS-Version auf 2.1.1 aktualisiert. Gleichzeitig stellte ich sicher, dass alle meine Eingabefelder, Schaltflächen, Tabellen und Überschriften eindeutige IDs hatten. Ich konnte dann alle Fundstellen (: xpath) entfernen, ohne die Tests zu unterbrechen.
Result 5
Diese Testsuite besteht jetzt zuverlässig die ganze Zeit! Genau das was ich wollte! Ja