В данной статье хочу поделиться опытом создания тестов с использованием фреймворка «Scalatest» для автоматизации тестирования. Статья будет состоять из 2х частей. Первая — пошаговая инструкция для создания и запуска базового теста, вторая — рассмотрение более сложных случаев и нюансов тестового стека, информация по созданию тестовых отчетов, решение возникающих проблем.
Существует множество решений для автоматизации тестирования. Каждое их них имеет свои особенности, преимущества, недостатки, различаются порогом вхождения, удобством применения, эффективностью, универсальность, кругом задач, для которых хорошо подходит. Для задачи автоматизации интеграционного тестирования и мониторинга систем для одного из проектов удачным решением оказалось применение связки «Scala» + «ScalaTest» + «SBT»
Задача:
Автоматизировать интеграционное тестирование;
Организовать мониторинг — автоматизированную проверку работоспособности/доступности информационной системы в целом и ее отдельных частей.
Обеспечить быструю локализацию проблемного компонента в случае отказа системы;
Предоставить отчет о выполнении теста в понятной форме.
Общий замысел таков: Выполняется некое действие, которое инициирует начало обработки в информационной системе (отправка сообщения сервису, менеджеру очередей, выполнение на веб-интерфейсе определенной последовательности действий). Система обрабатывает сообщение, прогоняя его через свои компоненты, подсистемы. Мы проверяем, что подсистема выполнила свою задачу по неким признакам: наличие записи в логе, наличие новой записи в БД, корректные ответы на запросы, получение ответных сообщений от системы, отображение на сайте. Каждая такая проверка оборачивается в шаг тестового сценария, который либо может быть успешен, либо провален.
После прохождения теста формируется отчет, к которому прикрепляются некоторые файлы (Ответы системы, скриншоты, результаты выполнения запросов), из которого можно определить, все ли хорошо в системе и где проблема. Примеры отчета представлены ниже:
Краткое описание используемых технологий:
Scala — язык программирования, спроектированный кратким и типобезопасным для простого и быстрого создания компонентного программного обеспечения, сочетающий возможности функционального и объектно-ориентированного программирования. Работает поверх jvm.
Sbt — (scala build tool) — система автоматической сборки для проектов, написанных на языках Scala и Java.
Scalatest — фреймворк для тестирования приложений. На главной странице читаем «simply productive» и «scalatest is designed to increase your team's productivity through simple, clear tests and executable specifications that improve both code and communications».
Selenium WebDriver — инструмент для тестирования web-приложений, набор средств для управления браузером, эмуляции пользовательских действий.
Этот стек порадовал универсальностью (разработка и запуск на win/linux/macos особенно не отличаются), богатством возможностей (если не получается реализовать что-то с помощью scala, можем использовать немного модифицированный java код и java библиотеки), эффективностью, поддержкой со стороны IDE (IntelliJ IDEA, Eclipse), хорошей документацией. Особенно порадовало, что это “Просто работает”, (если опустить проблемы с кодировкой в Windows), то есть количество проблем, решаемых бубном, не так велико, да и для начала работы со стеком необязательно иметь большой опыт в программировании и настройке информационных систем. Писать на Scala довольно приятно, особенно если использовать хорошую IDE.
На машине должна быть установлена Oracle JDK, желательно 8-й версии (1.8), на предыдущих версиях некоторые компоненты могут не завестись, на openJDK не будет работать Idea. www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html,
Для разработки теста будем использовать IntelliJ IDEA Community Edition от jetbrains.
Для этого ставим idea, запускаем, ставим плагин scala.
Перезапускаем приложение.
Создаем новый проект, Scala -> SBT.
Вводим имя, директорию, выбираем путь до JDK, ставим галку «Use auto-import», чтобы изменения в файле build.sbt сразу же актуализировались.
После этого, через некоторое время, будет создана структура проекта. В первый раз это может занять несколько минут, потому что Idea должна создать, проиндексировать проект, получить нужную версию компонентов из репозитория. Система может вывалить несколько Warning сообщений, игнорируем их.
Файл “build.sbt”, который создался в директории проекта, содержит информацию о проекте, а также список используемых компонентов.
Для подключения фреймворка “scalatest” добавим в “build.sbt” соответствующую строку. — libraryDependencies += «org.scalatest» % «scalatest_2.11» % «3.0.0-M7». Это последняя на момент написания статьи версия.
(SBT использует MAVEN репозиторий, поэтому найти библиотеку можно на сайте, к примеру, mvnrepository.com/artifact/org.scalatest/scalatest_2.11/3.0.0-M7).
(В SBT есть возможность подгружать автоматом последнюю версию, указав «latest.integration» в поле с версией. Но, это может грозить проблемами — изменением поведения при обновлении. Лучше фиксировать версию).
Прошу обратить внимание, что строки этого файла отделяются пустыми строками (Так принято, хотя без будет работать и без отделения строк).
После добавления, идея подберет соответствующую библиотеку из репозитория и подгрузит ее. Эта операция также может занять значительное время. Иногда возникают какие-то проблемы с обновлением, можно закрыть проект и открыть заново.
Индикатор в нижней части формы показывает готовность проекта. Создадим простой тест, посмотрим, как работает скалатест. Тестовые классы создаются в директории /src/test/scala из контекстного меню.
Назовем его «DummyTest». Текст класса:
/**
* Created by user on 26.08.2015.
*/
import org.scalatest.{Matchers, FreeSpec}
class DummyTest extends FreeSpec with Matchers{
"Два плюс три равно пяти" in {
val num = 2+3
num should be (5)
}
"Два плюс три равно четырем" in {
val num = 2+3
num should be (4)
}
}
Опишем тест. Из представленных здесь стилей мне по душе «FreeSpec». Его и будем использовать (добавляем «extends FreeSpec» после имени класса). «With Matchers» нужен для использования сравнений, к примеру, x should be (y) проверяет, что значение x совпадает со значением y. Более подробно сравнение значений здесь и здесь.
Тест содержит 2 шага. Первый — заведомо правильный, второй должен упасть. Шаг имеет название «Два плюс три равно пяти», которое отображается в отчетах. Это строка, которая заключена в кавычки, в ней допускается использование кириллицы. Слово in разделяет название шага и тело, заключенное в скобки "{","}". Тест считается пройдённым, если все шаги в теле прошли успешно. Тест считается заваленным, если что-то пошло не так и тест упал на шаге.
«val num = 2+3» — объявляем переменную и присваиваем ей значение.
В общем случае объявление в scala выглядит так:
val or val VariableName [: DataType] = Initial Value
val — значение, которое не меняется(похоже на константу), var — переменная — меняется в ходе выполнения кода. Использование val предпочтительнее. «num should be (5)» — сравнивает значения, проверяет, что значение переменной «num» равно пяти.
Если в строке с импортом отображается ошибка, то, вероятно, нужные библиотеки еще не загрузились, и идея не может импортировать нужные компоненты.
Выполним сценарий в Idea. Вызываем контекстное меню внутри класса — в нем появляется пункт «Run» (Запускать можно не любой класс, такую возможность предоставляет нам «extends FreeSpec» — использование trait FreeSpec). После этого класс добавится в список в правой верхней части экрана, там его можно запустить кнопкой, или сочетанием клавиш «Shift + f10»
Если работаем в windows — появится ошибка — проблема с кодировкой. 1251 — дефолтная виндовая кодировка не работает с кириллицей. Поэтому меняем на UTF-8 в правой нижней части окна. В диалоговом окне нажимаем «Convert». В linux/mac — проблемы быть не должно, utf-8 – кодировка по умолчанию для новых файлов в Idea.
Еще раз запускаем тест, смотрим результат в нижней части формы.
Видим некое дерево с проверками слева, описание ошибки в середине и результат справа. Ожидаемо второй шаг упал.
Запуск теста из IDE позволяет быстро отлаживать тесты в удобной форме. Но когда тест будет работать на сервере непрерывной интеграции, то запускаться он будет из консоли. Посмотрим, как сделать это на своей машине.
Для начала, поставим SBT, версии не ниже 0.13.8. Не рекомендуется ставить sbt из репозитория linux, потому что он там либо отсутствует, либо версия может быть устаревшей.
После установки sbt проверим версию, выполнив команду
"sbt --version"
.Так как в параметрах запуска sbt прописаны параметры «MaxPermSize», а в восьмой java он не нужен, то выпадет соответствующее предупреждение. Можем проигнорировать его или поменять настройки SBT в конфигурационных файлах. Версия “0.13.8” нас вполне устроит. Переходим в директорию SBT проекта. В ней выполняем команду для запуска тестов — «sbt test». Тест пройдет, прошедший шаг будет зеленым, зафейленный — красным.
Для стандартной консоли windows опять существует проблема кодировки. Чтобы отображать красиво русский текст, необходимо изменить кодировку страницы и шрифты. В свойствах окна командной строки ставим «Lucida Console».
В окне пишем команду «chcp 1251». После этого повторно выполняем «sbt test» еще раз — все должно отображаться красиво.
Также корректного отображения русских символов можно добиться, изменив конфигурационные файлы SBT. Базовая заготовка для теста готова.
Рассмотрим пример для проверки работоспособности сайта. Тест будет проверять, что возвращается страница и содержимое тега «title» правильное.
Проверим 2-мя способами — Get-запрос и selenium сценарий. Для реализации теста с использованием web-driver нужен “Firefox” (в принципе возможно использование других браузеров, в том числе «HtmlUnit», которому не нужен графический интерфейс, но с огнелисом проблем, как правило, меньше, да и в поставке многих линукс дистрибутивов он присутствует по умолчанию).
Для выполнения сценария Selenium, добавим в “build.sbt” строку
libraryDependencies += "org.seleniumhq.selenium" % "selenium-java" % "2.46.0"
. Идея начнет подтягивать из репозитория компоненты для работы Селениума.Создадим класс, назовем его, к примеру, “GetTest”. Импортируем нужные библиотеки и в начале класса определим метод “get” для получения текста страницы и создадим экземпляр «FirefoxDriver» (implicit value c драйвером для использования Selenium DSL, который есть в ScalaTest).
import org.openqa.selenium.WebDriver
import org.openqa.selenium.firefox.FirefoxDriver
import org.scalatest.selenium.WebBrowser
import org.scalatest.{Matchers, FreeSpec}
import scala.io.Source
class GetTest extends FreeSpec with Matchers with WebBrowser{
val pageURL = "http://scalatest.org/about"
def get(url: String) = Source.fromURL(url, "UTF-8").mkString
implicit val webDriver: WebDriver = new FirefoxDriver()
"Get запрос страницы %s и проверка заголовка".format(pageURL) in
{
get(pageURL) should include("<title>ScalaTest</title>")
}
"Открытие страницы " + pageURL+ " и проверка заголовка" in
{
go to pageURL
pageTitle should be ("ScalaTest")
quit()
}
}
Метод get получает код страницы от веб-сервера и преобразует в строку, в которой потом проверяется наличие подстроки "ScalaTest".
Второй тест открывает в огнелисе страницу и сверяет ее заголовок с шаблоном. Так как название шага — строка, то в нее можно добавлять переменные, склеивая строку, либо с использованием команды format. Правой кнопкой кликаем в классе — запускаем. Результат наблюдаем в нижней части формы.
Так можно быстро создать тест, который проверяет, что страница грузится и у нее правильный заголовок. Уже такой тест можно использовать для мониторинга сайта, запуская его с помощью сервера непрерывной интеграции по расписанию, рассылая уведомления заинтересованным лицам в случае падения.
Примеры действий, которые можно выполнять в тестах:
• GET/POST запрос. Проверка содержимого ответа;
• Запрос к базе данных. Проверка содержимого ответа;
• Генерация сообщений и их отправка в очередь;
• Получение сообщений из очереди. Проверка содержимого ответа;
• Проверка веб-интерфейса (Корректность работы, отображение значений на форме, получение скриншотов);
• Запуск сценариев командной строки (к примеру, мониторинг оставшейся памяти, пространства на жестком диске).
Для покрытия системы тестами нужно обернуть проверку каждого компонента (а в некоторых случаях, проверку каждого этапа обработки сообщений системой) в отдельный шаг или в отдельный тестовый класс, для точной локализации проблемы, для построения информативных отчетов.
Вопросы, которые будут рассмотрены в следующей статье:
• Генерация отчетов;
• Подключение конфигурационного файла;
• Параллельный запуск тестов;
• Запросы к БД;
• Работа с менеджером очередей;
• Более сложные сценарии с использованием Selenium WebDriver;
• Выполнение команд системы;
• Работа со временем (Паузы, ожидание, ограничение времени выполнения);
• Еще некоторые особенности работы стека.
Надеюсь, эта статья будет полезна тем, кто выбирает инструмент тестирования или мониторинга.
Полезные ссылки:
Официальная страница ScalaTest
Документация ScalaTest
Полезные книги:
Scala Cookbook
Testing in Scala
Комментарии (14)
afiskon
26.10.2015 17:28Спасибо за интересную статью. Но мне кажется, что тема использования всей мощи ScalaTest в ней не до конца раскрыта. С вашего позволения я рискну дополнить статью парой ссылок для заинтересованных читателей. По первой детально описывается DSL ScalaTest, по второй рассматривается вопрос написания с его помощью property based тестов.
ProfitFx
27.10.2015 10:16+1Благодарю за замечание! Действительно, фреймворк очень мощный, и статья охватила лишь малую долю возможностей. С вашего позволения, добавил еще ссылки на документацию и на пару книг. На странице ScalaTest можно посмотреть, какие еще плюшки есть у фреймворка.
afiskon
27.10.2015 10:24Testing in Scala хороша. Я бы еще обязательно добавил к ней ScalaCheck: The Definitive Guide. ScalaCheck — реально мощнейший и очень простой в использовании инструмент, который очень легко интегрируется в ScalaTest. Грех им не пользоваться.
nehaev
27.10.2015 14:33+1Интересно было бы узнать, в каких случаях ScalaCheck лучше обычных юнит-тестов?
afiskon
27.10.2015 14:37+1Всегда когда можно придумать property-based тест. Типичные примеры — валидация, сериализация/десериализация, сжатие, шифрование. Но при желании можно найти применение и в других областях. Так например проперти бейзед тестами доказывают корректность распределенных алгоритмов. Находят инвариант который всегда должен выполняться. И потом пишут тест в стиле — неважно что происходит с системой, для любой последовательности событий этот инвариант держится. Почему лучше обычных тестов — кода меньше, самих тестов в сотни раз больше. Больше покрытие, больше багов ловится, больше КПД программиста. Часто проверяются такие граничные случаи о которых программист и не подумал бы при написании теста вручную. Например что если передать на вход методу в качестве double значение NaN или + бесконечность и такого рода вещи.
Googolplex
26.10.2015 21:46+1Хочу отметить, что при создании SBT-проекта лучше не выбирать «Use auto import». Эта опция заставит среду перечитывать все проектные файлы практически на каждое изменение файла. Если в вашей сборке больше нескольких проектов, это будет делаться оооочень долго — например, в сборке на ~80 проектов обновление зависимостей делается около пяти минут. Лучше отключить эту опцию и запускать обновление вручную, через боковую панель. В конце концов, зависимости у проектов обычно обновляются не очень часто.
Elufimov
27.10.2015 09:18+1libraryDependencies += «org.scalatest» % «scalatest_2.11» % «3.0.0-M7». Это последняя на момент написания статьи версия.
Советовать людям ставить пререлизы идея плохая. Последняя стабильная версия 2.2.4. Кстати писать можно «org.scalatest» %% «scalatest» % «2.2.4» Так подтянется версия библиотеки под ту версию scala которая указана в проекте.
Импортируем нужные библиотеки и в начале класса определим метод “get” для получения текста страницы и создадим экземпляр «FirefoxDriver».
И создаём implicit value c драйвером для использования selenium dsl который есть в scalatest. Наверно это нужно было указать, а то тем кто не знает будет не сильно понятно куда делся браузер.
Конечно как-то странно при разборе библиотеки видеть мини экскурсы в язык и кучу скриншотов которые съели почти всё. Но радует само наличие статьи, а то про scalatest мола что есть в рунете.ProfitFx
27.10.2015 10:43Спасибо за замечания, с Вашего позволения добавил уточнение про браузер.
Целью создания статьи является не столько обзор фремворка, сколько инструкция по применению. Как начать пользоваться ScalaTest. Понятно, что для специалистов, которые работают c java, для которых среда разработки Idea как родная, а проблемы с кодировкой давно решены и изучены, большая часть статьи — это очевидные вещи. Для новичков же начать пользоваться технологией не всегда так просто, непонятно, как подступиться ко всему этому хозяйству.
К примеру, на странице scalatest.org/user_guide/running_your_tests описано 8! способов запуска тестов. И неочевидно, какой из них лучше использовать.
Изначально статья — это немного причесанная пошаговая инструкция для сотрудника отдела тестирования в нашей компании, как сделать тест для проверки определенной функциональности. Для разработчика она избыточна.Elufimov
27.10.2015 10:46Наверно меня смутило название претендующее на раскрытие фишек фреймворка. Посмотрим что будет в след статьях.
egor_masalitin
Русский текст в названиях тестов использовали только для примера, или для разработки тоже?
ProfitFx
Егор, спасибо за вопрос! Написание шагов на русском языке хорошо тем, что любое заинтересованное лицо может интерпретировать результат. Поэтому в наших тестах, которые мониторят ресурсы, используем русский язык. Это одно из требований к тестам.
egor_masalitin
Введение в команду иностранных граждан не планируется? Вопрос чисто на интерес, а не критика.