Особенности тестирования Single Page Applications

Давайте обсудим как тестировать Single Page Applications - приложения построенные преимущественно на JavaScript, где страница практически не перезагружается. Самым известным приложением такого рода является GMail, а в последнее время их становится всё больше и больше, благо возможности браузеров позволяют исполнять JavaScript очень быстро. Каждое уважающее себя веб-приложение старается выполняться без перезагрузки страниц.

Пользоваться ими крайне удобно - всё быстро, страница умеет обновляться по частям, и пр. А вот как тестировать? Вот в чем вопрос. Конечно, WebDriver всё скушает, это без вопросов. Но, в SPA у нас практически всё будет работать асинхронно, а значит постоянно нужно вставлять какие-то ожидания, и далеко не факт, что в итоге тесты получатся стабильными и быстрыми (быстрыми точно не получатся).

Для создания SPA используются JavaScript фреймворки: Backbone, Marionette, AngularJS, EmberJS - тысячи их. И в зависимости от фреймворка (и приложения) могут быть свои механизмы обеспечения тестирования. Например, командой AngularJS был создан проект Karma.

Мне б хотелось узнать: у кого есть какой опыт тестирования такого рода приложений? Использовали ли вы возможности фреймворка?

1 лайк

Ну и с зачином - отвечу сам на свой же вопрос :smile:

Я работаю с EmberJS. В нем есть такая замечательная вещь как RunLoop, позволяющая все асинхронные операции производить синхронно. Ember приложение может быть помещено в контейнер на странице с тестами, и выглядеть вот так:

Это картинка. Тыц! Эй, как нормально вставить картинку?

(справа в квадратике - показывается что происходит в приложении, слева - список тестов)

Сами тесты я пишу на CoffeeScript. Выглядят они красиво и лаконично:

 
test 'should comment on ticket', ->
  createTicket title: 'Functional tests'
  seeDetailsPage()
  see "Functional tests", 'h1'
  fillIn '#ticket textarea', "What tests do you need?"
  clickButton 'Comment', '.left'
  see 'Commented', '.timepanel'
  see 'What tests do you need?', 'p.post'
  andThen ->
    find('#ticket textarea').val().should.be.empty

Методы see, clickButton, createTicket - мои. В свою очередь Ember предоставляет методы find, click, fillIn. Используя jQuery эти методы позволяют эмулировать действия пользователя в приложении. Все вызовы в этом тесте - асинхронны, see сработает только после того как до конца отработает clickButton.
Чтобы сказать Эмберу - “дождись пока всё отработает” - можно использовать andThen. Т.е команда ’ find(‘#ticket textarea’).val().should.be.empty’ будет выполнена только когда все необходимые действия будут выполнены.

Итого: в асинхронном приложении получился абсолютно синхронный код. Тесты работают крайне быстро - всё происходит на одной веб странице браузере, нет никаких задержек и ожиданий. Используя Karma, Teaspoon, или другие раннеры, тесты можно выполнить в консоли и получить результат.

P.S. К сведенью: этот форум тоже написан на EmberJS :slight_smile:

1 лайк

А в чем вобщем-то отличие Single Page, от прочих?

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

У нас использовался подобный же подход (код) на основе ZombieBrowser, но инструмент перестал поддерживаться и бажики доняли. Хотя приложение и не SP, но состоит из контекстов которые при тестировании выглядели именно как SP

  it "should destroy instance", async ->
    instanceId = @browser.post("#{@applicationId}/launch").id
    @browser.navigate "#{instanceId}"
    wait => expect(@browser).to.have.text "Running"
    @browser.header().click 'Destroy'
    wait =>
      expect(@browser.dialog()).to.have.text /Do you really want to destroy/
      @browser.dialog().click 'Destroy'
    wait =>
      expect(@browser.dialog()).to.have.text /scheduled for destruction/
      @browser.dialog().click 'Close'
    wait => expect(@browser).to.have.text "Destroyed"

async - явное использование файберов.
по факту после навигейт, весь тест происходит на одной странице, которая живет своей жизнью.
@browser зомби, с расширенными методами домена. Например dialog, по факту просто сужения дома, с которым идет работа.

Возможно после вашего поста, посмотрим и в сторону эмбер. :slight_smile:

P.S. CoffeeScript рулит!

Не знаю, будет ли для вас актуально, раз вы пишете на JavaScript.
Но на Java есть тестовая библиотека Selenide - она как раз идеально подходит для решения таких проблем.

Пример теста:

$(byText("ConfetQA demo!")).shouldBe(visible);

Фокус Selenide в том, что любые методы типа shouldBe, shouldHave, если условие сразу не выполняется, могут немножко подождать (по умолчанию до 4 секунд). Это как раз решает проблему динамической загрузки контента. Таким образом, тестировщику не надо вообще забивать себе голову всякими ожиданиями - это делается автоматически, причём ровно столько, сколько надо. Отчего тесты становятся и быстрыми, и надёжными.

Вот здесь как раз есть пример тестирования Gmail.

5 лайков

Тем, что браузер никогда не знает догрузилась ли страница полностью или нет. Каждый раз JavaScript на клиенте создает веб интерфейс, а значит между тем как загрузилась веб страница и она становится доступной проходит некоторое время. Дальнейший переход по ссылкам меняет только структуру DOM но не перезагружает страницу.

Это чисто EmberJS + QUnit. Файберы в Ембере называются промисами (я так понимаю, эти понятия синоничны).

Тесты запускаются непосредственно в браузере. Так как приложение живет на одной веб странице, нам нужно только раз открыть эту страницу, вгрузить веб-приложение и пройтись по нему тестами.

К сожалению, хороших ссылок не нашел, зато есть такая вот презентация гле сравнивается тестирование через Capybara->Selenium с Konacha->EmberJS. Testing Ember Apps

Да, это прикольно, но в случае с EmberJS клиент-сайд тестирование всё равно будет оптимальнее. Приложение само уведомит нас, когда страница отрендерится. Итого, нам не надо постоянно обращаться к элементу и проверять появился он или нет. То есть, JavaScript в данном случае не мой личный выбор, а необходимость. Тесты с Selenium будут в любом случае идти медленнее - открывать браузер, перегружать страницу, постоянно ждать элемент, вместо того чтобы прогнать все тесты на одной странице.

Что меня интересует - кто-то тестировал приложения на базе AngularJS? Есть ли там какие механизмы?

Да, говорят, AngularJS идеально подходит для тестирования.
На XP Days был небольшой доклад как раз на эту тему: TDD with AngularJS and Karma

Доброго дня.

Тестирую приложение на AngularJS. Приложение пока не большое, но уже сейчас озадачились автоматизацией.
Для автотестов используем ProtractorJS - библиотека для NodeJS, обертка для WebDriverJS. Библиотека специально заточена для работы с Angular, имеет кучу полезных методов для работы. Ссылка на ProtractorJS - GitHub - angular/protractor: E2E test framework for Angular apps

Сейчас написано около 30 тестов. Спрашивайте если интересует что-то конкретное.

Появилась новая проблема - нужно мерять производительность клиентской стороны Angular приложения. Ищу инструменты и способы вытягивать скорость работы javascript из браузера.

3 лайка

2014 год… прослезился )

1 лайк

Простите за глупый вопрос, что актуально в 2019 по этой теме?

Немного поделюсь своим текущим опытам. Тестирую сайт, Single Page application, состоящий из backend - внутренние сервисы и front-end - ReactJs. Для автоматизации использую просто Selenium WebDriver + NUnit и PageObjects. С моей точки зрения, основная хитрость - это надо дожидаться родительского обьекта, прежде чем продолжать работать с конкретным обьектом. По этой причине у меня задефенировано много веб элементов, в основном использую методы типа WaitForElementPresent().

Здравствуйте.
Поделитесь, пожалуйста, примерами кода.

Да нет никаких особенностей)
Юзаю Селенид+TestNG в 1 поток.
Единственное - главное чтобы на странице были спиннеры, чтобы на них можно было завязаться. Это гарантия того, что страница прогрузилась.
А так - как обычные страницы.