2014-01-23 1 views
5

Когда я запускаю свои спецификации «вебинара», они, кажется, всегда прошли, но если я попробую весь набор, он пройдет только один из тестов около 50% или времени. Я тестировал это, используя одно и то же семя каждый раз, чтобы увидеть, имеет ли он какое-либо отношение к порядку, в котором выполняются тесты.Тест Rspec требует сна для работы

Если я замедляю свой тест, поспав посреди его, он волшебным образом начнет прохождение 100% снова. Очевидно, я не хочу полагаться на слабую работу, как это, и хочу понять, как на самом деле исправить мою проблему. требуют "spec_helper"

require "spec_helper" 

describe "ProgramManager::Webinars" do 
    let(:program) { create(:program) } 
    let(:superuser) { create(:superuser) } 

    describe "#index" do 
    before { login_as(superuser) } 
    let(:delete) { 'Delete' } 

    it "displays an edit and destroy link for all webinars" do 
     w1, w2, w3 = create(:webinar, program: program), create(:webinar, program: program), create(:webinar, program: program) 
     visit program_webinars_path(program) 
     [w1, w2, w3].each do |webinar| 
     expect(page).to have_link webinar.name, href: edit_program_webinar_path(program, webinar) 
     expect(page).to have_link '', href: destroy_warnings_program_webinar_path(program, webinar) 
     end 
    end 

    it "has a link to create a new webinar" do 
     visit program_webinars_path(program) 
     expect(page).to have_content 'New Webinar' 
    end 

    it "deletes a webinar", js: true do #THIS IS THE TEST THAT SOMETIMES FAILS 
     webinar = create(:webinar, program: program) 
     visit program_webinars_path(program) 
     all('.destroy').last.click 
     wait_for_ajax 
     sleep(1.second)   #THIS IS THE SLEEP THAT FIXES THE FAILURE 
     expect { click_link delete }.to change(Webinar, :count).by(-1) 
    end 
    end 

.

FactoryGirl.define do 
    factory :webinar do 
    program 
    name "some name" 
    url "some url" 
    description "some description" 
    speaker "some speaker" 
    starts_at Time.now 
    end 
end 

.

FactoryGirl.define do 
    factory :program do 
    contract 
    program_manager factory: :user 
    sequence(:name) { |n| "Program-#{n}" } 
    description  { "Program description" } 
    starts_at  { Time.now } 
    ends_at   { Time.now + 10.days } 
    color   { "#33f" } 
    program_type { "some program type" } 
    verified  { false } 
    end 
end 

.

<div class="col-md-4"> 
    <%= link_to "<span class='glyphicon glyphicon-plus'></span>".html_safe, new_program_webinar_path(@program), class: 'new-webinar', data: { toggle: 'tooltip', title: 'Add a Webinar' } %> 
    <h4>Current Webinars</h4> 

    <% if @webinars.empty? %> 
    <p>There are currently no webinars to display.</p> 
    <% else %> 
    <table class="table table-condensed"> 
     <% @webinars.each do |webinar| %> 
     <tr> 
      <%= content_tag :td, class: pm_setup_classes(webinar, @webinar) do %> 
      <%= link_to "<span class='glyphicon glyphicon-remove'></span>".html_safe, destroy_warnings_program_webinar_path(@program, webinar), class: 'destroy', data: { toggle: 'modal', target: '#ajax-modal' } %> 
      <%= link_to webinar.name, edit_program_webinar_path(@program, webinar), class: 'webinar' %> 
      <% end %> 
     </tr> 
     <% end %> 
    </table> 
    <% end %> 

    <%= link_to 'New Webinar', new_program_webinar_path(@program), class: 'btn btn-success btn-block' %> 
</div> 

.

class ProgramManager::WebinarsController < ProgramManager::ApplicationController 
    before_filter :authenticate_user! 
    before_filter :webinars 

    def new 
    @webinar = @webinars.build 
    clean_webinars 
    end 

    def create 
    @webinar = @program.webinars.build(params[:webinar]) 
    clean_webinars 
    if @webinar.save 
     redirect_to program_webinars_path(@program), success: "Webinar successfully created" 
    else 
     render :new 
    end 
    end 

    def edit 
    @webinar = @program.webinars.find(params[:id]) 
    end 

    def update 
    @webinar = @program.webinars.find(params[:id]) 
    if @webinar.update(params[:webinar]) 
     redirect_to program_webinars_path(@program), success: "Webinar successfully updated" 
    else 
     render :edit 
    end 
    end 

    def destroy 
    @webinar = @program.webinars.find(params[:id]) 

    if @webinar.destroy 
     redirect_to program_webinars_path(@program), success: "Webinar removed successfully" 
    else 
     render :index 
    end 
    end 

    def destroy_warnings 
    @webinar = @program.webinars.find(params[:id]) 
    render layout: false 
    end 

private 

    def clean_webinars 
    @webinars = @webinars.delete_if(&:new_record?) 
    end 

    def webinars 
    @webinars = @program.webinars 
    end 
end 

Прошу прощения, так много кода, связанного с этим вопросом. Я просто пытаюсь предоставить как можно больше информации, так как я понятия не имею, от чего эта ошибка или как ее исправить.

+0

Вы пытались настроить эту настройку? 'Capybara.default_wait_time = 5' (по умолчанию - 2 с). См. Doc в разделе «Асинхронный JavaScript (Ajax и друзья)» в https://github.com/jnicklas/capybara – steakchaser

+0

Хотя это также работает, я чувствую, что это слишком обходное решение, чем решение.Этот код является частью большой программы с тысячами тестов, поэтому я не хочу, чтобы один тест из тысячи заставил их всех идти медленнее. –

+0

Я не думаю, что это замедлит остальные ваши тесты, если они не будут ожидая, что элемент не будет на странице. Капибара все еще собирается проверить элемент; это просто изменяет продолжительность проверки, а не интервал между проверками. – steakchaser

ответ

1

Проблема в конечном счете заключается в том, что javascript затухает. Кнопка удаления, которую мы пытаемся нажимать, находится на модальном, который затухает, чтобы предупредить вас о последствиях вашего удаления и попросит вас подтвердить. Наш помощник wait_for_ajax() ждал, пока все активные соединения jQuery не будут разрешены. Соединения завершатся так, что они перейдут к следующей строке кода, которая сообщила ему щелкнуть ссылку на ссылку удаления. В html была ссылка на удаление, поэтому Capybara может ее найти, но так как она активно исчезает ... клик не работает и тест терпит неудачу!

+3

Мы столкнулись с подобной проблемой и избежали ее, добавив '= javascript_tag '$ .fx.off = true;' если Rails.env.test? 'в наш' layout/application.html.haml', это отключит все анимации для тестирования – BooVeMan

0

Вы можете настроить Capybara.default_wait_time = 5 (по умолчанию - 2 с).

См документ в «Asynchronous JavaScript (Ajax и друзей)» раздел

Это изменит общее количество времени, Капибара ждет, прежде чем давать на поиск узла, но не должны влиять на интервал, в котором он будет пытаться проверить.

+0

Все еще сталкивается с той же проблемой. Мой сотрудник сказал, что тест по-прежнему не удался для него со сном (1 секунда), видимо, –

+0

Вы используете драйвер Poltergeist? – steakchaser

+0

Да, да. Мы считаем, что проблема связана с постепенным исчезновением модальности, когда кнопка удаления включена. Он пытается получить щелчок, прежде чем он полностью исчезнет, ​​и будет сделан и не удастся. Отключение функции Fade устраняет нашу проблему. –

0

Это может быть асинхронная операция в вашей базе данных. Мы получали случайные сбои в наших спецификациях, пока не установили fsync = on в нашей конфигурации PostgreSQL. Я думаю, что это лучший вариант, чем набивать сон (#) повсюду.

0

Я не вижу трассировку ошибки, но если не все данные готовы на странице, вы можете использовать конкретный gem под названием rspec-wait, чтобы подождать какое-то условие (по умолчанию 3 секунды). Так, например, rspec код становится следующим:

visit program_webinars_path(program) 
    wait_for(page).to have_content 'New Webinar' 

Это позволит вам ждать некоторых конкретных HTML (при необходимости) в течение времени.