[Из песочницы] Функциональные тесты: Django + Selenium WebDriver и 3 варианта на Ваш выбор

image


«В жизни каждого django-разработчика наступает момент, когда он решительно рвет со своим прошлым, лишенным функционального тестирования!»


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

В статье рассматриваются 3 варианта функционального тестирования django приложения на python 3.4 под django 1.7 с помощью Selenium WebDriver (на Хабре есть замечательные статьи с подробным описанием возможностей Selenium тут и тут).

В тестах использовались технологии и приложения: VirtualBox 4.3.6, Debian 7.7, virtualenv 12.0.7, python 3.4, django 1.7, selenium webdriver 2.47.3, iceweasel, xvfb, PyVirtualDispley 0.1.5, PhantomJS 1.9.8, nginx 1.2.1, uWSGI.

Рассмотрим следующие связки для Debian 7.7 + virtualenv 12.0.7 + django 1.7 +:

  • 1 вариант. X.org + Selenium WebDriver + Firefox (Iceweacel для debian) — с графической оболочкой
  • 2 вариант. Terminal + Selenium WebDriver + Xvfb, PyVirtualDisplay — запуск через терминал
  • 3 вариант. Terminal + Selenium + PhantomJS — запуск через терминал


Инструменты функционального тестирования позволяют провести оценку работоспособности конечного сайта и поведения страниц в браузере, а так-же помогают если базовый тестовый клиент Django оказывается не удобен при тестировании динамически подгружаемых данных (при использовании JavaScript, Ajax). Когда различные технологии объединены в едином продукте и возникает закономерный вопрос, как они поведут себя вместе в том или ином случае.

При помощи этих инструментов мы можем симулировать реальное поведение пользователей.

Приступим!

В виртуальное окружение ставим Selenium:

pip install selenium


В нашем случае будем тестировать процесс аутентификации пользователя, поэтому файл test.py создадим в приложении autentication (Пункт Б).

Вариант 1. Визуальное симулирование поведения пользователя


А. Предполагаем, что графический интерфейс установлен, тут — X.org.

Ставим Debian Iceweasel (ранее Debian Firefox — модификация браузера Mozilla Firefox в Debian GNU/Linux):

sudo apt-get install iceweasel


! Примечание:
При попытке запустить тест из терминала, без запуска графической среды, мы получим исключение: raise WebDriverException(«The browser appears to have exited „selenium.common.exceptions.WebDriverException: Message: The browser appears to have exited before we could connect. If you specified a log_file in the FirefoxBinary constructor, check it for details.)


Б. Создаем файл test.py.

Вариант 1
# Подключить встроенный сервер Django для использования клиента Selenium 
from django.test import LiveServerTestCase
# Подключить вебдрайвер управления браузером (тут FireFox)
from selenium import webdriver
import time
        
class SeleniumTests(LiveServerTestCase):
    def test_auth(self):
        # Подключить webdriver Firefox
        br = webdriver.Firefox()
        # Перейти на главную страницу, получив адрес сервера 'localhost:8081' и полный URL
        br.get('%s%s' % (self.live_server_url, '/'))
                
        # Перейти по ссылке регистрации
        br.find_element_by_xpath('//a[@href="/register/"]').click()
        # Подождать 3 секунды
        time.sleep(3)

        # Регистрация пользователя
        # Найти поле username и указать значение 'new'
        br.find_element_by_id('username').send_keys('new')
        # Найти поле email и указать значение 'new@new.ru'
        br.find_element_by_id('email').send_keys('new@new.ru')
        # Указать пароль в 2-ух полях
        br.find_element_by_id('password1').send_keys('12345678')
        br.find_element_by_id('password2').send_keys('12345678')
        # Перейти по ссылке регистрации
        br.find_element_by_id('btn_register').click()

        # Активизировать пользователя в тестовой БД
        pis = Myuser.objects.get(username='new')
        # Поставить признак пользователя - Активен
        pis.is_active = True
        # Сохранить в БД
        pis.save()

        # Перейти на домашнюю страницу
        br.find_element_by_xpath('//a[@href="/"]').click()
        # Подождать 3 секунды
        time.sleep(3)

        # Войти под зарегистрированным пользователем
        br.find_element_by_id('username').send_keys('new')
        br.find_element_by_id('password').send_keys('12345678')
        br.find_element_by_name('Вход').click()
        # Проверить наличие имени авторизованного пользователя в соответствующем теге
        assert br.find_element_by_xpath('//a[@data-content="Личный кабинет"]').text == 'new'
                
        # Отключить вебдрайвер, закрыть браузер
        br.quit()



! Примечание:
В тексте программы необходимо ставить паузу time.sleep(3) для избежания ошибки: “selenium.common.exceptions.NoSuchElementException: Message: Unable to locate element: {»method":«id»,«selector»:«username»}", «Не удается найти элемент», вызванной тем, что не успел подгрузиться весь контекст страницы. При возникновении ошибки можно увеличить время ожидания до 5-10 сек.


ВРЕМЯ РАБОТЫ ТЕСТА (Debian 7.7, VirtualBox, 12 Mb VideoRAM, 512 Mb RAM, 1 core): (29.89 + 25.65 + 26.73)/3 = 27.42 сек

Вариант 2. Запуск тестов через терминал без запуска графической оболочки на xvfb + pyvirtualdisplay


А. Для запуска из консоли необходимо установить:
Устанавливаем в виртуальном окружении virtualenv х-сервер: xvfb и виртуальный дисплей: pyvirtualdisplay.

sudo apt-get install xvfb
pip install pyvirtualdisplay


! Примечание:
PyVirtualDisplay ставим от имени локального пользователя, иначе при тестировании из-под пользователя в строке импорта: from pyvirtualdisplay import Display, будет ошибка.
PyVirtualDisplay 0.1.5 официально поддерживает версии python: 2.6, 2.7, 3.2, 3.3, проверено на 3.4 — работает


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

Вариант 2
# Подключить виртуальный дисплей
from pyvirtualdisplay import Display

# Подключить встроенный сервер Django для использования клиента Selenium 
from django.test import LiveServerTestCase
# Подключить вебдрайвер управления браузером (тут FireFox)
from selenium import webdriver
import time

class SeleniumTests(LiveServerTestCase):
    def test_auth(self):
        # Инициализировать и запустить виртуальный дисплей
        display = Display(visible=0, size=(800, 600))
        display.start()

        # Подключить webdriver Firefox
        br = webdriver.Firefox()
                
        # Перейти на главную страницу, получив адрес сервера 'localhost:8081' и полный URL
        br.get('%s%s' % (self.live_server_url, '/'))
                
        # Перейти по ссылке регистрации
        br.find_element_by_xpath('//a[@href="/register/"]').click()
        # Подождать 3 секунды
        time.sleep(3)
        # Регистрация пользователя
        # Найти поле username и указать значение 'new'
        br.find_element_by_id('username').send_keys('new')
        # Найти поле email и указать значение 'new@new.ru'
        br.find_element_by_id('email').send_keys('new@new.ru')
        # Указать пароль в 2-ух полях
        br.find_element_by_id('password1').send_keys('12345678')
        br.find_element_by_id('password2').send_keys('12345678')
        # Перейти по ссылке регистрации
        br.find_element_by_id('btn_register').click()

        # Активизировать пользователя в тестовой БД
        pis = Myuser.objects.get(username='new')
        # Поставить признак пользователя - Активен
        pis.is_active = True
        # Сохранить в БД
        pis.save()

        # Перейти на домашнюю страницу
        br.find_element_by_xpath('//a[@href="/"]').click()
        # Подождать 3 секунды
        time.sleep(3)

        # Войти под зарегистрированным пользователем
        br.find_element_by_id('username').send_keys('new')
        br.find_element_by_id('password').send_keys('12345678')
        br.find_element_by_name('Вход').click()
        # Проверить наличие имени авторизованного пользователя в соответствующем теге
        assert br.find_element_by_xpath('//a[@data-content="Личный кабинет"]').text == 'new'
                
        # Остановить виртуальный дисплей
        display.stop()
        # Отключить вебдрайвер, закрыть браузер
        br.quit()



ВРЕМЯ РАБОТЫ ТЕСТА (Putty SSH, Screen, virtualenv, Debian 7.7, VirtualBox, 12 Mb VideoRAM, 512 Mb RAM, 1 core): (28.5 + 25.82 + 24.98)/3 = 26.43 сек

Вариант 3. Запуск тестов через терминал без запуска графической оболочки на PhantomJS


А. Для запуска из консоли необходимо установить phantomjs:

cd /usr/local/share
sudo wget https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-1.9.8-linux-x86_64.tar.bz2
sudo tar xjf phantomjs-1.9.8-linux-x86_64.tar.bz2
sudo ln -s /usr/local/share/phantomjs-1.9.8-linux-x86_64/bin/phantomjs /usr/local/share/phantomjs
sudo ln -s /usr/local/share/phantomjs-1.9.8-linux-x86_64/bin/phantomjs /usr/local/bin/phantomjs
sudo ln -s /usr/local/share/phantomjs-1.9.8-linux-x86_64/bin/phantomjs /usr/bin/phantomjs


И запускаем:

phantomjs -v


! Примечание:
Если полученный пакет не годится данной системе, получим ошибку: «Не могу запустить бинарный файл» (Часто из-за попытки установить на 32-ух разрядную систему (x86) 64-разрядное приложение (x64)).


Б. Создаем файл test.py. К ранее описанному в Варианте 1 добавляем строки для работы с PhantomJS.

Вариант 3
# Подключить PhantomJS 
from selenium.webdriver import PhantomJS

# Подключить встроенный сервер Django для использования клиента Selenium 
from django.test import LiveServerTestCase
import time
# НЕ НУЖЕН
# Подключить вебдрайвер управления браузером (тут FireFox)
#from selenium import webdriver

class SeleniumTests(LiveServerTestCase):
    def test_auth(self):
        # НЕ НУЖЕН
        # Подключить webdriver Firefox
        #br = webdriver.Firefox()

        # Инициализировать драйвер PhantomJS
        br = PhantomJS()
        # установить разрешение страницы
        br.set_window_size(800, 600)

        # Перейти на главную страницу, получив адрес сервера 'localhost:8081' и полный URL
        br.get('%s%s' % (self.live_server_url, '/'))
        # Перейти по ссылке регистрации
        br.find_element_by_xpath('//a[@href="/register/"]').click()
        # Подождать 3 секунды
        time.sleep(3)

        # Регистрация пользователя
        # Найти поле username и указать значение 'new'
        br.find_element_by_id('username').send_keys('new')
        # Найти поле email и указать значение 'new@new.ru'
        br.find_element_by_id('email').send_keys('new@new.ru')
        # Указать пароль в 2-ух полях
        br.find_element_by_id('password1').send_keys('12345678')
        br.find_element_by_id('password2').send_keys('12345678')
        # Перейти по ссылке регистрации
        br.find_element_by_id('btn_register').click()

        # Активизировать пользователя в тестовой БД
        pis = Myuser.objects.get(username='new')
        # Поставить признак пользователя - Активен
        pis.is_active = True
        # Сохранить в БД
        pis.save()

        # Перейти на домашнюю страницу
        br.find_element_by_xpath('//a[@href="/"]').click()
        # Подождать 3 секунды
        time.sleep(3)

        # Войти под зарегистрированным пользователем
        br.find_element_by_id('username').send_keys('new')
        br.find_element_by_id('password').send_keys('12345678')
        br.find_element_by_name('Вход').click()
        # Проверить наличие имени авторизованного пользователя в соответствующем теге
        assert br.find_element_by_xpath('//a[@data-content="Личный кабинет"]').text == 'new'
                
        # НЕ НУЖЕН
        # Отключить вебдрайвер, закрыть браузер
        # br.quit()



Время работы теста (Putty SSH, Screen, virtualenv, Debian 7.7, VirtualBox, 12 Mb VideoRAM, 512 Mb RAM, 1 core): (14.64 + 14.89 + 13.05)/3 = 14.19 сек.

image



Резюме:


Использование Варианта 3 — [14.19 сек.] предоставляет почти в 2 раза меньше времени на перекуры, чем Вариант 2 — [26.43 сек.] и Вариант 1 — [27.42 сек.]. Но Вариант 1 — визуально информативнее.

ПС. Тут нужно дополнительно учесть, что в коде теста в общей сложности использованы 6 сек ожидания.

© Habrahabr.ru