2014-05-09 21 views
7

Ich verwende Rails 4.0.5, rspec 2.14.1, Capybara 2.2.1, Capybara-Webkit 1.1.0 und database_cleaner 1.2.0. Ich sehe ein merkwürdiges Verhalten beim folgenden Funktionstest (der simuliert, dass ein Benutzer einen Kommentar zu einem Beitrag anzeigt, über ein Symbol schwebt, um ein Menü anzuzeigen, und auf einen Menüeintrag klickt, um den Kommentar zu löschen):Warum führt das Hinzufügen von "sleep 1" in einem after hook dazu, dass dieser Rspec/Capybara-Test bestanden wird?

let(:user){create(:user)} 
let(:post){create(:post, author: user)} 
let!(:comment){create(:comment, post: post, author: user)} 

... 

it "can delete a comment" do 
    assert(page.has_css? "#comment-#{comment.id}") 
    find("#comment-#{comment.id}-controls").trigger(:mouseover) 
    find("#comment-#{comment.id} .comment-delete a").click 
    assert(page.has_no_css? "#comment-#{comment.id}") 
end 

Dieser Test schlägt ungefähr 80% der Zeit fehl, immer weil ein Datensatz aus der Datenbank als nil - ich bekomme NoMethodError: undefined method X for nil:NilClass, für verschiedene Werte von X abgerufen wird. Manchmal ist der Nullpunkt der Kommentar, der gelöscht wird, manchmal ist es der Beitrag dass der Kommentar angehängt ist, manchmal ist es der Autor des Kommentars/Beitrags.

Wenn ich sleep 1 bis zum Ende des Tests hinzufügen, es passiert:

it "can delete its own comment" do 
    assert(page.has_css? "#comment-#{comment.id}") 
    find("#comment-#{comment.id}-controls").trigger(:mouseover) 
    find("#comment-#{comment.id} .comment-delete a").click 
    assert(page.has_no_css? "#comment-#{comment.id}") 
    sleep 1 
end 

Es geht auch, wenn ich sleep 1 in einem Block after setzen.

Irgendeine Idee, warum ich diese NoMethodErrors bekomme, und/oder warum der Test bestanden wird, wenn ich ihn für eine Sekunde schlafen lasse, nachdem alle Arbeit erledigt ist?

+2

Welche Codezeile ist der 'NoMethodError'-Ursprung? Es ist komisch, wenn man 'sleep' am * Ende * behebt, aber Capybara Race Conditions sind ein häufiges Problem. –

+0

@BuckDoyle Alle Fehler sagen 'Fehler/Fehler: Es konnte keine übereinstimmende Zeile aus Backtrace gefunden werden ', gefolgt von Verweisen auf verschiedene Model/Controller-Methoden, die von einer AJAX-Anfrage aufgerufen werden, die ausgelöst wird, wenn ein Kommentar gelöscht wird. Es sieht so aus, als wäre es eine Race-Bedingung, nicht zwischen der Capybara-Assertion und der Kommentarlöschung, sondern zwischen dem Datenbank-Teardown und dem Callback-Code, der nach dem Löschen ausgeführt wird. –

Antwort

5

Ich vermute, dass es in der Anwendung möglich ist, für den Kommentar von der Seite zu verschwinden (was das letzte, was Sie behaupten), bevor er aus der Datenbank gelöscht wird. Das bedeutet, dass der Test vor dem Löschen bereinigen kann. Wenn dies der Fall ist, können Sie es beheben, indem Sie auf das tatsächliche Löschen am Ende des Tests warten. Ich habe diese Methode, um (a Reimplementation a method that was removed from Capybara 2 but is still sometimes necessary)

def wait_until(delay = 1) 
    seconds_waited = 0 
    while ! yield && seconds_waited < Capybara.default_wait_time 
    sleep delay 
    seconds_waited += 1 
    end 
    raise "Waited for #{Capybara.default_wait_time} seconds but condition did not become true" unless yield 
end 

so kann ich

wait_until { Comment.count == 0 } 

in Tests durchführen.

Ein anderer Ansatz besteht darin, eine Rack-Middleware hinzuzufügen, die nach dem Ende des Tests gestellte Anforderungen blockiert. Dieser Ansatz wird hier im Detail beschrieben: http://www.salsify.com/blog/tearing-capybara-ajax-tests Ich habe gesehen, dass er sehr gut die Datenverluste in einer umfangreichen RSpec-Feature-Spezifikation beheben kann.

+1

Eigentlich wird der Kommentar immer gelöscht, bevor er verschwindet, aber das Verschwinden löst einen AJAX-Rückruf aus, der (glaube ich) mit der Säuberung der Datenbank läuft. Der von Ihnen verknüpfte Ansatz sieht vielversprechend aus. Ich werde es ausprobieren. Vielen Dank! –

+1

Capybara.default_wait_time ist jetzt zugunsten von Capybara.default_max_wait_time veraltet – Karen

-1

https://github.com/jnicklas/capybara#asynchronous-javascript-ajax-and-friends

When working with asynchronous JavaScript, you might come across situations where you are attempting to interact with an element which is not yet present on the page. Capybara automatically deals with this by waiting for elements to appear on the page.

+2

Das erklärt nicht, warum das Setzen von sleep am * end * das Problem behebt, nachdem irgendwelche Behauptungen gemacht wurden. Deshalb habe ich das ursprüngliche Poster nach weiteren Details gefragt. –

+0

Aber sagt, dass Sie nicht schlafen sollten. Warum sollten Sie Ihre Tests absichtlich verlangsamen? Capybara beschäftigt sich mit dem Warten auf Elemente wirklich sehr gut per phoet – Dono

Verwandte Themen