2012-09-23 10 views
12

Ho un test di integrazione di Rails che non funziona e non riesco a capire perché. Sto usando Capybara con Selenium come driver.Test di integrazione di Capybara con JavaScript asincrono

Il test verifica che il contenuto della pagina sia stato rimosso dopo una chiamata AJAX. L'azione rilevante è che un pulsante viene cliccato, e quel clic sul pulsante fa sì che una sezione della pagina venga rimossa tramite una chiamata jQuery remove(). Ecco un'approssimazione del codice di test di integrazione:

click_button("Remove stuff") 
assert has_no_link?("This should be removed") 

L'affermazione non riesce, il che implica che il collegamento esiste ancora.

Ho letto su Capybara e so che è possibile prolungare il tempo di attesa predefinito. L'ho esteso a un valore ridicolo (20 secondi), eppure l'asserzione fallisce.

quando seguo il processo di test io manualmente, il fonte della pagina mostra ancora il contenuto, ma il DOM non lo fa (visualizzando DOM Inspector di Firefox e alla ricerca per l'elemento). È questo il problema? Ho anche provato a controllare il DOM mentre i test sono in esecuzione in Firefox per verificare se il contenuto era lì, e non sembra essere.

Non ho idea di come Capybara trovi ancora questo collegamento che non esiste più nel DOM. Capybara sta esaminando la fonte invece del DOM e trovando il collegamento lì? Se è così, non ho idea di come risolvere questo test per assicurarmi che il test passi. Aggiornare la pagina risolverebbe il problema, ma non è esattamente quello che farebbe un utente, quindi esito a cambiare pagina solo per fare passare il test ...

Mi piacerebbe qualche consiglio su come affrontare questo problema.

Grazie!

+1

Hai finalmente trovato una soluzione al tuo problema? Ne ho uno simile e mi chiedo se puoi essere di qualche aiuto per me. –

+1

No, sfortunatamente no. Ho speso troppo per far funzionare quel singolo test, quindi ho finito per commentare, invece di risolverlo. Scusate, non posso essere più di aiuto ... Non ho avuto l'energia per immergermi profondamente dopo aver passato così tanto tempo! Sentiti libero di rispondere alla domanda se lo capisci e sarò felice di darti credito! – aardvarkk

+0

Ti sei ricordato di includere 'js: true' nella tua rspec? – RubeOnRails

risposta

0

C'è un modo pulito per controllare che le richieste di ajax siano fatte, cosa che ho appreso dal this article. Invece di wait con un tempo specifico, è possibile utilizzare la funzione ajax $.active (che è not in the actual API but is exposed in modo da poterlo utilizzare). $.active indica il numero di connessioni attive a un server, in modo che quando si scende a zero si conosce la richiesta Ajax è fatto:

wait_until do 
    page.evaluate_script('$.active') == 0 
end 

Se questo non funziona, allora il problema è da qualche altra parte (che a giudicare dalle quello che hai scritto sembra probabile). Se il cambiamento sta avvenendo solo nel DOM, allora devi assicurarti che javascript sia abilitato per il tuo test/specifica. In rspec, ad esempio, si imposta :js => true per farlo; in Cucumber aggiungi una linea sopra lo scenario con @javascript. Non uso i test di default delle rotaie ma ci deve essere un'impostazione per fare lo stesso.

+0

Hmm ... Sto usando il driver Selenium sotto Capybara, che per impostazione predefinita supporta il test di JavaScript (consultare https://github.com/jnicklas/capybara#drivers). Ho provato ad aggiungere il tuo codice di attesa, ma non è stato risolto. Buone idee però! Non ancora risolto ... – aardvarkk

+0

Usi il capibara per qualsiasi altro test che coinvolga javascript? –

+0

Sì, ma questo è l'unico che cerca le informazioni * rimosse *. Tutte le altre cose sembrano funzionare ... Ad esempio, controllando che le query AJAX generino nuovi pulsanti o contenuti modificati. Ma qualcosa su jQuery 'remove()' non sembra stare bene. Se esamino 'page.html', mostra ancora il contenuto lì, quindi c'è sicuramente qualcosa che non va qui. – aardvarkk

0

Stai prima testando qualcos'altro con il link e poi testando che sia stato rimosso?

In altre parole, è il test qualcosa di simile:

has_no_link = $('#id_for_link') 
//.... some test 
click_button("Remove stuff") 
assert has_no_link?("This should be removed") 

Se questo è il caso, allora has_no_link sarà ancora puntare al link. remove() lo rimuoverà dal DOM, ma la variabile lo punta ancora in memoria.

Si dovrebbe richiedere nuovamente il collegamento nel DOM per vedere se si ottiene un risultato.

0

Ho dovuto riscrivere wait_until per un test che implicava l'attesa di una richiamata attivata dallo streaming di un video di YouTube in un determinato punto. Ecco quello che ho usato:

# in spec_helper.rb 
require "timeout" 
def wait_until time=0.1 
    Timeout.timeout(Capybara.default_wait_time) do 
     sleep(time) until value = yield 
     value 
    end 
end 
+1

Per le versioni più recenti di Capybara, si noti che 'default_wait_time' è stato deprecato. Usa 'default_max_wait_time' ora. – Sia

0

paio di approcci ho pensato di:

1: Controllare la versione capibara e cercare eventuali bug con la versione

2: Forse provare a fare un ritrovamento sul link dopo il pulsante viene premuto

click_button ("Rimuovi roba") find. (: xpath, '// a [di testo() =' link deve essere rimosso ') dovrebbe be_false

3: utilizzare has_link? invece di has_no_link?

click_button ("Rimuovi roba") page.has_link? ("Link deve essere rimosso"). Dovrebbe be_false

0

Puoi sempre farlo manualmente. Utilizzando idea di @ shioyama:

def wait_for_ajax 
    timer_end = Time.now + 5.seconds 
    while page.evaluate_script('$.active') != 0  
     if Time.now > timer_end 
     fail "Page took more than 5 seconds to load via ajax" 
     end 
     sleep 0.1 
    end 
    end 
3

Thoughtbot ha un grande post sul blog in attesa di AJAX, che potete leggere here, anche se si basa su Rspec, e sembra che si sta utilizzando TestUnit.

Funziona perfettamente in situazioni in cui Capybara non aspetta abbastanza a lungo, ma non aggiunge timeout inutilmente lunghi. Io lavoro per lo più in Rspec ora, ma penso che si può modificarlo in questo modo:

# Create this file in test/support/wait_for_ajax.rb 
module WaitForAjax 
    def wait_for_ajax 
    Timeout.timeout(Capybara.default_max_wait_time) do 
     loop until finished_all_ajax_requests? 
    end 
    end 

    def finished_all_ajax_requests? 
    page.evaluate_script('jQuery.active').zero? 
    end 
end 

sia È possibile includere quando necessario nel file singolo test, o utilizzare una delle strategie previste nel this SO post per includere automaticamente ogni volta.

Quindi, ogni volta che si esegue un test che non attende correttamente il completamento di AJAX, è sufficiente inserire la riga wait_for_ajax. Usando il tuo codice come esempio:

click_button("Remove stuff") 
wait_for_ajax 
assert has_no_link?("This should be removed")