Как стать автором
Обновить

Тестирование веб-проектов. jsFUnit

Время на прочтение8 мин
Количество просмотров1.4K
Тестирование веб-проектов. jsFUnit

В профессиональном программировании огромную роль играют автоматезированные тесты. Они заменяют и команду профессиональных тестеров и подсказывают о возникших неполадках в других частях
обширного проекта во время разработки. Работу веб-приложения необходимо тестировать на разных браузерных движках: Gecko, Presto, KHTML, WebKit и Trident.

Разновидностью автоматизированных тестов являются функциональные тесты. Их главная особенность — эмуляция работы пользователя с приложением
через интерфейс. В веб-программировании для написания функциональных тестов используют Selenium. Огромный недостаток Seleniuma — это его невысокая скорость выполнения таких тестов.

Более быстрым фреймворком оказался jsUnit. Правда, jsUnit предназначен для написания unit-тестов, а не функциональных. Обладает он и рядом других мелких недостатков. Как то —
обращение к тестам по их пути, а не выбор теста из списка. В случае хоть одной ошибки весь ползунок закрашивается в красный цвет, а не разделялся на зелёные/красные участки, соответствующие успешно/не успешно выпоненным тестам.

Всё это привело к созданию jsFUnit.



jsFUnit состоит из интерфейса для запуска тестов и двух библиотек: библиотеки проверки условий (различные функции assert) и библиотеки функций эмулирующих работу пользователя с веб-интерфейсом. И скрипта на perl
(update.pl), регистрирующего новые тесты.

jsFUnit я расположил на сурцефорже, откуда и предлагаю загрузить его всем желающим: http://sourceforge.net/projects/jsfunit/files/

После того как скачаете jsFUnit-2.1.zip, распакуйте его в public_html вашего веб-проекта. У вас появится каталог public_html/tests. Надеюсь, что все читатели осведомлены, что такое public_html. Но для тех, кто случайно начал читать статью, предлагаю взять виндовую машину, установить на неё xampp из http://www.apachefriends.org/en/xampp.html в c:\xampp и распаковать jsFUnit-2.1.zip в c:\xampp\htdocs.

В браузере откройте http://localhost/tests (или же какой другой адрес имеет ваш хост). Вы увидите интерфейс jsFUnit. Он довольно простой и вы легко в нём разберётесь.

Для создания теста:
1. Добавте тест в каталог tests/tests-js
2. Тест должен иметь расширение .js
3. Выполните update.pl (он поставляется с jsFUnit и находится в каталоге tests. Это скрипт на perl. Для начинающих поясню:
Пуск -> Выполнить…
cmd
c:\xampp\perl\bin\perl.exe c:\xampp\htdocs\tests\update.pl
)
4. Обновите http://localhost/tests (F5)

Как выглядят тесты?

jsFUnit
похож на jsUnit, jUnit, CUNIT, PHPUnit, NUnit, PyUnit, fUnit, DUnit,
FPCUnit, Test::Class и Test::Unit, CPPUnit, FlexUnit, COSUnit и т.п.

Собственно файл с тестом имеет одну или несколько функций с префиксом test в названии. Которые последовательно запускаются.

function testNagan() {

}

function testBlaster() {

}

В функциях описываются манипуляции с веб-приложением и проверки на правильность реакции веб-приложения.

function testNagan() {
var nagan = new Nagan()
assertEquals(«Название экземпляра Nagan не типа 'строка'. Соболезнуем», typeof(nagan.name), «string»)
}

Если требуется в начале каждого теста сделать какие-либо инициализирующие действия можно использовать setUp.
А в конце — tearDown.

function setUp() {
protiv_loma_net_prijoma = new Lom()
}

function Nagan() {
assert(«Где вы так метать лом научились?», protiv_loma_net_prijoma.rasstojanie_v_metrax(20))
}

function Sablja() {
assert(«Да вы совсем слабенький, батенька!», protiv_loma_net_prijoma.rasstojanie_v_metrax(1.5))
}

function tearDown() {
protiv_loma_net_prijoma.polozhit()
}

А вот функции jsFUnit для проверок


assert([комментарий], значение) — Если значение 0, "", undefined, false или null, то assert прекращает выполнение теста и выводит ошибку в Лог с комментарием, если комментарий указан. Условие должно быть выражением возвращающим значение типа boolean. Пример: assert(«Страшная ошибка!!!», a==10 || x<2)
assertTrue([комментарий], условие) — синоним assert
assertFalse([комментарий], условие) — эквивалент assert, но ошибкой считается, когда условие истинно
assertEquals([комментарий], ожидаемое_значение, полученное_значение) — ошибка, если полученное значение не совпадает с ожидавшимся
assertNotEquals([комментарий], ожидаемое_значение, полученное_значение) — ошибка, если полученное значение совпадает с ожидавшимся
assertIdentity([комментарий], ожидаемое_значение, полученное_значение) — ошибка, если полученное значение не тождественно с ожидавшимся
assertNotIdentity([комментарий], ожидаемое_значение, полученное_значение) — ошибка, если полученное значение тождественно с ожидавшимся
assertNull([комментарий], значение) — ошибка, если значение не null
assertNotNull([комментарий], значение) — ошибка, если значение — null
assertUndefined([комментарий], значение) — ошибка, если значение не undefined
assertNotUndefined([комментарий], значение) — ошибка, если значение — undefined: assertNotUndefined(undefined)
assertNaN([комментарий], значение) — ошибка, если значение не NaN
assertNotNaN([комментарий], значение) — ошибка, если значение — NaN: assertNotNaN(Number.NaN)
fail(комментарий) — ошибка
warn(сообщение, [значение]) — Пишет в лог красным цветом сообщение. Если указано значение, то выводит его как «сообщение: значение»
inform(сообщение, [значение]) — Пишет в лог зелёным цветом
info(сообщение, [значение]) — Пишет в лог зелёным цветом
debug(сообщение, [значение]) — Пишет в лог синим цветом

Функции манипулирования интерфейсом


Данные функции подобны некоторым функциям Seleniumа

Функции для манипулирования окнами и элементами

Функции, заканчивающиеся на Def, в случае неудачного завершения возвращают false, а не вызывают исключение (см. assert).

function testOpen() {
win = getWindow(«MAIN») // Первое, что делает пользователь, это открывает страницу веб-приложения.
// getWindow ждёт пока страница полностью не загрузится
// Страница должна быть в том же домене в котором находится jsFUnit

iframe = getFrameWindow(win, «frame_for_tables») // Иногда необходимо бывает и с фреймами поработать
}

function testForm() {
select(win.document.getElementById(«City»), «St. Peterburg») // выбираем город из выпадающего списка
clickToButton(win, «Применить») // нажимаем на кнопку [Применить]
var pwin = openButton(win, «Пересчитать») // нажимаем на кнопку [Пересчитать] и ждём пока загрузится окно с именем Pereschitatq
clickToLink(pwin, «Меню 1», 2) // Нажимаем на второй элемент на странице pwin с innerHTML = «Меню 1»
}

А теперь подробнее:

getWindowDef(name, [time])

getWindow(name, [time])

Возвращает окно по его имени
  • name — имя окна. Параметр подвергается транслитерации. Например: «Алка*@дмин „ -> “ALKA__DMIN»
  • time — сколько ждать полной загрузки страницы в окне.
    Необязательный параметр. 5000 по умолчанию. Если страница загрузится быстрее чем за time, то ждать она не будет
  • window.name нужно присвоить транслитерированный window.document.title. Благодаря чему в name можно указывать для таких окон их заголовок. Это делается функцией set_translit_name_window()
  • window.document.title — это заголовок окна браузера
  • window.name — имя окна. Должно быть идентификатором, то есть: /[a-z]\w*/i или пусто


Пример:


Окно:
<html>
<head>
<title>@@Админ или как** </title>
<script>
window.name = "__ADMIN_ILI_KAK__"
</script>
</head>
<body>
</body>
</html>

Тест:
win = getWindow("@@Админ или как** ")

waitCondition(cond, time) — ожидать выполнения cond

если cond не станет true за time милисекунд — выход

cond работает только с глобальными переменными, если задан строкой. Для локальных переменных используйте функцию:
var a = 10, b = 10
waitCondition(function(){ return a == b }, 500)

sleep(time)

спать time милисекунд. Аналогичен:
waitCondition(«false», 500)

waitLoadWindowDef(win, time)

waitLoadWindow(win, time)

ожидает загрузки окна win time милисекунд. Если окно загрузится быстрее, чем за time, то ожидание завершиться тот час же
  • win – объект окна. Используйте window.frames[«имя фрейма»] или window.open(), чтобы его получить
  • time – время в милисекундах


Внимание! Фунцкция getWindow уже использует waitLoadWindow. waitLoadWindow нужно использовать после window.open:
win = window.open(«localhost/cgi-bin/mypage.pl», «name_window»)
waitLoadWindow(win, 5000)

getFrameWindowDef(win, frame, [time])

getFrameWindow(win, frame, [time])

Ждёт загрузки страницы в указанный фрейм и возвращает объект его окна
  • win — окно в котором находится фрейм
  • frame — имя фрейма, заданное атрибутом name или id
  • time — необязательный параметр. Задаёт время ожидания загрузки страницы в фрейм. По умолчанию — 5000 милисекунд


Пример:


Страница в win:
<html>
<body>
<iframe name=«frame_tab» src=«page_for_this_frame»></iframe>
</body>
</html>

Тест:
var win_in_frame = getFrameWindow(win, «frame_tab»)

selectDef(sel, text)

select(sel, text)

Выбирает опцию тега <select> по её тексту
sel – элемент. Получить его можно по имени: select(document.all.reason, «Вот так»)
<select name=«reason»>
<option value=1>А вот и нет
<option value=2>Вот так
</select>

Того же можно добиться и так: document.all.reason.value = 2

linkDef(win, text_in_link, [tag], [event], [number])

link(win, text_in_link, [tag], [event], [number])

возвращет ссылку на элемент с текстом text_in_link: <div>text_in_link</div> или <a>text_in_link</a> или в других элементах
  • tag и event — параметры необязательные
  • tag — теги, среди которых будет производиться поиск. Например: «input». По умолчанию – все ("*")
  • event представляет собой строку кода
  • event можно устанавливать в link.innerText, link.innerHTML, link.value, link.options[link.selectedIndex].text, link.title и т.п.
  • number — номер найденного элемента. Так 1 (по умолчанию) — первый найденный, 2-й — второй найденный и т.д.


Пример:


Окно:
<html>
<head>
<script>
window.name=«Admin»
</script>
</head>
<body>
<div id=Div1>Пол<b>ь</b>зователи</div>
<div id=Div2>Пол<b>ь</b>зователи</div>
</body>
</html>

Тест:
win = getWindow(«Админ») // Получаем окно
div = link(win, «Пользователи», 2) // получаем второй элемент с текстом «Пользователи» в нём (Div2)
div.style.color = «red» // делаем его красного цвета

clickToLinkDef(win, text_in_link, [number])

clickToLink(win, text_in_link, [number])

нажимает на элемент указанный по контенту можно указать на какой по счёту элемент нажимать (number)

Пример:


Окно:
<a href="/users">Пользователи</a>

Тест:
clickToLink(win, «Пользователи»)

clickToButtonDef(win, text_in_value, [number])

clickToButton(win, text_in_value, [number])

нажимает на кнопку можно указать на какой по счёту элемент нажимать (number)

Пример:


Окно:
<input type=«button» value=«Пользователи»>

Тест:
clickToButton(win, «Пользователи»)

openLink(win, text_in_link, [sleepTime], [time], [number])

нажимает на элемент и возвращает объект одноимённого окна
  • win — окно в котором нажимать
  • text_in_link — текст в элементе
  • sleepTime — сколько ждать до того как начать запрашивать окно. По умолчанию — 0 милисекунд
  • time — время отпущенное на ожидание открытия окна. По умолчанию — 5000 милисекунд
  • number — номер найденного элемента. По умолчанию — первый


Пример:


Окно:
<script src=«tests/WorkWithHtml.js»>
<script>
window.name = translate(«Пользователи»)
// window.name = «Polqzovateli»
</script>

Тест:
win = openLink(win, «Пользователи»)

openButton(win, text_in_value, [sleepTime], [time], [number])

нажимает на кнопку и возвращает объект одноимённого окна
  • win — окно в котором нажимать
  • text_in_value — название кнопки
  • sleepTime — сколько ждать до того как начать запрашивать окно. По умолчанию — 0 милисекунд
  • time — время отпущенное на ожидание открытия окна. По умолчанию — 5000 милисекунд
  • number — номер найденной кнопки. По умолчанию — первый


Выводы



1. Недостатки Selenium-а:
а) требуется устанавливать сервер
б) требователен к ресурсам
в) много времени тратится на запуск сервера + запуск браузера
г) сервер Selenium-а перезапускает браузер для каждого теста

2. Недостатки jsUnit:
а) для каждого теста создаётся html-обёртка
б) для запуска нескольких тестов требуется написать новый тест с функцией suite(), в которой перечислить вызываемые
в) путь к тесту вводится в TestRunner (интерфейсе jsUnit) каждый раз вручную
г) отладочная информация выводятся в отдельное окно, а не в общий лог ошибок

3. Достоинства jsFUnit:
а) не требует сервера
б) не требователен к ресурсам
в) тест или группа тестов выбираются из выпадающего списка
г) группа тестов, которые нужно запустить просто кладутся в отдельный каталог
д) отладочная информация выводится в общий лог
е) можно визуально оценить количество выполненных тестов через закраску ползунка

Таким образом jsFUnit быстр, удобен в использовании и тем самым экономит ваше время!
Теги:
Хабы:
Всего голосов 16: ↑7 и ↓9-2
Комментарии31

Публикации

Истории

Ближайшие события

7 – 8 ноября
Конференция byteoilgas_conf 2024
МоскваОнлайн
7 – 8 ноября
Конференция «Матемаркетинг»
МоскваОнлайн
15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань