Подтвердить что ты не робот

Интеграция с Capybara с асинхронным JavaScript

У меня есть тест интеграции Rails, который не работает, и я не могу понять, почему. Я использую Capybara с Selenium в качестве драйвера.

Тест проверяет, что содержимое страницы было удалено после вызова AJAX. Соответствующее действие состоит в том, что нажата кнопка, и этот щелчок кнопки вызывает удаление части страницы с помощью вызова jQuery remove(). Здесь приведена аппроксимация кода тестирования интеграции:

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

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

Я читал на Capybara, и я знаю, что вы можете продлить время ожидания по умолчанию. Я распространил его на нелепое значение (20 секунд), и все же утверждение терпит неудачу.

Когда я сам выполняю тестовый процесс вручную, источник страницы все еще показывает содержимое, но DOM не делает (просматривая Firefox DOM Inspector и ищет элемент). Это проблема? Я даже попытался проверить DOM, пока тесты запущены в Firefox, чтобы проверить, был ли контент там, и это не похоже.

Я не знаю, как Capybara все еще находит эту ссылку, которая больше не существует в DOM. Капибара изучает источник вместо DOM и находит ссылку там? Если да, я не знаю, как исправить этот тест, чтобы убедиться, что тест проходит. Обновление страницы устранит проблему, но это не совсем то, что сделает пользователь, поэтому я не решаюсь изменить страницу, просто чтобы пройти тест...

Хотелось бы получить рекомендации относительно того, как подойти к этой проблеме.

Спасибо!

4b9b3361

Ответ 1

Thoughtbot имеет отличную запись в блоге в ожидании AJAX, которую вы можете прочитать здесь , хотя он основан на Rspec, и он выглядит как вы используете TestUnit.

Он отлично подходит для ситуаций, когда Capybara не достаточно долго ждет, но не добавляет излишне длительных тайм-аутов. Сейчас я работаю в основном в Rspec, но я думаю, вы можете изменить его, выполнив следующее:

# 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

Вы можете включить его, когда это необходимо в отдельном тестовом файле, или использовать одну из стратегий, приведенных в этом сообщении SO, для автоматического включения его каждый раз.

Затем, всякий раз, когда у вас есть тест, который неправильно ожидает завершения AJAX, просто вставьте строку wait_for_ajax. Использование кода в качестве примера:

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

Ответ 2

появился какой-то метод wait_until, но он был устаревшим в последнее время и был изменен с помощью метода синхронизации.

http://www.elabs.se/blog/53-why-wait_until-was-removed-from-capybara

https://github.com/jnicklas/capybara/blob/master/lib/capybara/node/base.rb#L44

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

Ответ 3

Там есть аккуратный способ проверить, выполняются ли запросы ajax, которые я узнал из в этой статье. Вместо wait с некоторым конкретным временем вы можете использовать функцию ajax $.active (которая не в фактическом API, а выставлена ​​, чтобы вы могли ее использовать), $.active сообщает вам количество активных подключений к серверу, поэтому, когда он падает до нуля, вы знаете, что запрос ajax выполняется:

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

Если это не сработает, проблема будет где-то в другом месте (что, судя по тому, что вы написали, кажется вероятным). Если изменение происходит только в DOM, вам необходимо убедиться, что javascript включен для вашего теста/спецификации. Например, в rspec вы устанавливаете :js => true для этого; в Cucumber вы добавляете строку над сценарием с помощью @javascript. Я не использую тесты по умолчанию для рельсов, но должна быть настройка, чтобы сделать то же самое.

Ответ 4

Вы сначала проверяете что-то еще со ссылкой, а затем проверяете, что она удалена?

Другими словами, ваш тест что-то вроде:

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

Если это произойдет, то has_no_link все равно укажет на ссылку. remove() удалит его из DOM, но ваша переменная все еще указывает на него в памяти.

Вы должны запросить ссылку еще раз в DOM, чтобы узнать, получаете ли вы результат.

Ответ 5

Мне пришлось переписать wait_until для теста, который включал ожидание обратного вызова, вызванного потоковой передачей видео на YouTube. Вот что я использовал:

# 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

Ответ 6

Несколько подходов, о которых я думал:

1: Проверьте версию capybara и найдите ошибки с вашей версией

2: Возможно, попробуйте выполнить поиск по ссылке после нажатия кнопки

click_button ( "Удалить материал" ) find (: xpath, '//a [text() =' Ссылка должна быть удалена '). should be_false

3: Использовать has_link? вместо has_no_link?

click_button ( "Удалить материал" ) page.has_link? ( "Ссылка должна быть удалена" ). should be_false

Ответ 7

Вы всегда можете сделать это вручную. Использование идеи @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