Testes de integração não confiáveis / Flakey Capybara / AngularJS com problemas de tempo
Atualmente esses testes são flakey.
Às vezes eles passam. Às vezes eles falham.
Abaixo está a configuração, o código e a saída que demonstram esse problema.
As sugestões para superar esse problema serão muito apreciadas e tenho certeza de que ajudarão muitos outros, por isso, 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)
Coisas que tenteiCertifique-se de que eu uso os correspondentes DSL em espera da CapivaraVerifique se meu limpador de banco de dados está configurado corretamenteTeste todos os itens da página, supondo que ele possa não estar na página e ainda estar carregandoReduzir testes inconsistentesExecutar teste inconsistente sozinhoIdentifique os DSLs de Capivara de código que são o gatilho para resultados de teste inconsistentes.
ou seja, criando um novo registro e assumindo que a página foi redirecionada e que o registro está na página click_onou
.click não 'constantemente' trabalhandoAtualize a Capivara para a versão mais recente (em uma ramificação separada)Poltergeist atualizado e RSpec para a versão mais recente (em uma ramificação separada, ainda trabalhando nisso)Recursos que eu usei[1]Capivara O DSL
[2]Dicas de Capivara, PhantomJs, Poltergeist e Rspec
E muitos mais...
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
Resultado dos testesTest Run 1: Failing
Opções de execução: inclua {: focus => true} exclua {: slow => true}
Todos os exemplos foram filtrados; ignorando {: focus => true}
Mostrar um novo custeio na lista, mostra o custeio após a criação mostra os detalhes do novo custeio após a criação (FAILED - 1)
Falhas:
1) Mostre um novo custo na listagem,
mostra os detalhes do novo custo após a criação
Falha / Erro: expect (page) .to have_content ("test1")
espera que #has_content? ("test1") retorne verdadeiro, ficou falso
# ./spec/integration/costings/show_costing_spec.rb:20:in block (3 levels) in
# ./spec/integration/costings/show_costing_spec.rb:19:in block (2 levels) in
Concluído em 5,46 segundos 2 exemplos, 1 falha
Test Run 2: Passing
Opções de execução: inclua {: focus => true} exclua {: slow => true}
Todos os exemplos foram filtrados; ignorando {: focus => true}
Mostrar um novo custo na listagem,
mostra o custo após a criação
mostra os detalhes do novo custo após a criação
Concluído em 3,57 segundos 2 exemplos, 0 falhas
Atualização 1Gems de teste atualizados para as seguintes versões:
capivara (2.6.2) a partir de (2.1.0)
database_cleaner (1.5.1) de (0.7.1)
debug_inspector (0.0.2)
empacotador de guarda (0.1.3)
carga de fígado de guarda (1.2.0)
especificação de proteção (2.1.2)
jasminerice (0.0.10)
pág (0.17.1)
phantomjs (2.1.1.0)
poltergeist (1.9.0) de (1.4.1)
trilhos-transferidor (0.0.17)
alavanca (0.10.3) de (0.9.12)
rack (1.4.7)
teste de rack (0.6.3)
trilhos (3.2.21)
rails-assets-angular (1.4.9) de (1.3.20)
rspec-trilhos (3.4.2) de (2.11.4)
simplecov (0.8.2)
rodas dentadas (2.2.3)
zeus (0.13.3)
zeus-parallel_tests (0.2.1)
Result 1
Infelizmente, atualizar essas gemas não pareceu fazer diferença e meus testes ainda estavam em perfeitas condições.
Atualização 2Eu implementei as sugestões de Tom Walpole. Garante que meu admin_sign_in aguarde a conclusão de login.
Também atualizei minha configuração do database_cleaner, como sugeriu Tom.
Result 2
Para minha pilha, essas mudanças não pareciam ter efeito.
Nota: Se alguém não estiver usando o AngularJS, sinto que essas alterações teriam feito diferença. Então, obrigado Tom por suas sugestões.
Atualização 3Eu precisava obter mais informações sobre o que estava acontecendo durante minhas execuções de teste. Existem sugestões na rede para registrar, usar gemas de economia de captura de tela e coisas do tipo, mas eu não achava que essas seriam as mais eficientes em termos de tempo. Eu queria especificar onde queria que o teste fosse pausado e exibir o conteúdo de variáveis e campos de formulário. Em um navegador seria o ideal.
O que eu usei
Eu estava usando "save_and_open_page" e "print page.html" para depurar.
O que eu mudei para
Como eu estava executando o RSpec 3.4.2, adicionei um método auxiliar de depuração:
rails_helper.rb
def debugit
puts current_url
require 'pry'
binding.pry
end
Result 3
Um URL seria impresso no console e o teste seria pausado. Nesta fase, eu seria capaz de navegar para a URL, fazer login, navegar até a página de teste e ver o que o teste da Capybara havia feito.
Isso me permitiu identificar que a origem dos meus problemas surgiu quando o teste estava usando o DSL fill_in da capivara. Em algumas execuções de teste, os campos seriam preenchidos corretamente e o formulário seria enviado. No outro cenário, o formulário seria preenchido corretamente, mas o botão enviar seria pressionado muito rapidamente. O resultado aqui é que um registro foi criado, mas os campos de entrada de nome e descrição não foram mantidos.
Atualização 4Descobri que, porque estava usando formulários e tabelas de entrada AngularJS, o AngularJS exigia um pouco de tempo para vincular aos campos de entrada. Se não fosse permitido desta vez, os dados de entrada não seriam salvos.
A Capivara fornece métodos de espera como "dentro" e "encontrar". Usei-os, mas eles não ajudaram no problema do tempo de ligação do AngularJS. Eu achei ng-if poderia ser usado para criar uma instrução if para aguardar um item específico que significaria as ligações do AngularJS aos campos do formulário completos.
Então, usei os métodos de espera da Capybara para aguardar os campos que queria preencher e usei o ng-if do AngularJS para não mostrar os campos até que estejam prontos.
Implementação
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
Os testes finalmente passam! No entanto, tenho todos esses métodos de localização com o xpath, garantindo que itens específicos e difíceis de segmentar sejam esperados em ...
Atualização 5Embora no meu gemfile eu estivesse executando o gem phantomJS versão 2.1.1, minha versão da linha de comando era apenas 1.X. Isto provou ser significativo.
Atualizei minha versão do phantomJS da linha de comando para 2.1.1. Ao mesmo tempo, assegurei que todas as minhas caixas de entrada, botões, tabelas e títulos tivessem IDs únicos. Pude remover todas as ocorrências de localização (: xpath) sem interromper os testes.
Result 5
Agora, esse conjunto de testes passa com confiabilidade o tempo todo! Exatamente o que eu queria! Sim!