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

Тестирование Django приложений с помощью Selenium

Время на прочтение 6 мин
Количество просмотров 6.9K


Selenium


Selenium — это очень удобный (имхо) инструмент для тестирования веб-приложений.

Тесты создаются с помощью дополнения для Firefox, которое может генерировать код теста на различных языках, в том числе и на Python. Затем эти тесты выполняются специальным сервером, Selenium RC.

Сам по себе Selenium не привязан ни к языкам ни к фреймворкам, поэтому чтобы интегрировать его в систему тестирования Django-приложений, нужно приложить очень небольшие усилия.

Для решения задачи интеграции я буду использовать библиотеку Django: Sane Testing. Это библиотека, расширяющая возможности стандартной системы тестирования Django, в том числе и поддержкой тестов Selenium.


Ставим Django: Sane Testing


Установка этой библиотеки была бы стандартной, если бы она не тянула за собой Django. Что не есть правильно, если вы пользуетесь транком, как и я.

Скачиваем актуальный релиз, распаковываем (можно в /tmp), открываем setup.py, находим

install_requires = ['Django>=1.0_final','nose>=0.10'], <br/>
либо удаляем, либо комментим часть про Django. После этого ставим:

sudo python setup.py install



Ставим Selenium


Нужно поставить сервер и плагин для Firefox. И то, и другое находится на странице загрузки.

Для установки плагина достаточно открыть ссылку Selenium IDE, разрешить установку плагина, установить его и перезапустить Firefox. Кроме того, уважаемый bazzzman написал расширение для этого плагина (!), для удобства работы.

Для установки сервера нужно скачать архив по ссылке Selenium RC, распаковать его куда-нибудь. В получившемся после распаковки каталоге будет несколько подкаталогов. Среди них будет selenium-server-x.y.z, это каталог с сервером. Сам сервер — это файл selenium-server.jar. Вот его куда-нибудь копируем, в удобное место, например в /usr/local/lib, после чего весь распакованный каталог можно потереть, он больше не нужен.

Для удобного запуска создаём скрипт,

sudo nano /usr/local/bin/selenium-server

с таким содержимым:

#!/bin/bash
/usr/bin/env java -jar /usr/local/lib/selenium-server.jar

Задаём ему права на выполнение:

sudo chmod 755 /usr/local/bin/selenium-server

Проверим: после выполнения команды /usr/local/bin/selenium-server должно появляться что-то эдакое:

23:58:17.788 INFO - Java: Sun Microsystems Inc. 14.2-b01
23:58:17.798 INFO - OS: Linux 2.6.31-14-generic i386
23:58:17.863 INFO - v1.0.1 [2696], with Core v@VERSION@ [@REVISION@]
23:58:18.111 INFO - Version Jetty/5.1.x
23:58:18.113 INFO - Started HttpContext[/selenium-server/driver,/selenium-server/driver]
23:58:18.114 INFO - Started HttpContext[/selenium-server,/selenium-server]
23:58:18.114 INFO - Started HttpContext[/,/]
23:58:18.166 INFO - Started SocketListener on 0.0.0.0:4444
23:58:18.166 INFO - Started org.mortbay.jetty.Server@fc9944


если так и есть, значит сервер работает ОК.



Тестовый проект


Создаём тестовый проект:

django-admin.py startproject justtotest

В проекте будет одно приложение, назовём его habrahabr:

./manage.py startapp habrahabr

Наш тестовый хабр будет состоять из одной модели:

class Greeting(models.Model):
    text = models.CharField(max_length=200)


одной формы для ввода приветствия:

class GreetingForm(forms.ModelForm):
    text = forms.CharField(widget=forms.TextInput(attrs={'size':'40'}),
                           error_messages={'required': 'А текст приветствия ввести ? '})
    class Meta:
        model = Greeting


и одной вьюшки:

def Greetings(request):
    template_name = 'habrahabr/index.html'
    title='Selenium'
    if request.method == 'GET':
        form = GreetingForm()
    else:
        form = GreetingForm(request.POST)
        if form.is_valid():
            text = form.cleaned_data['text']
            new_greeting=Greeting(text=text)
            new_greeting.save()
            return HttpResponseRedirect('/')
    greetings = Greeting.objects.all().order_by('-id')
    context = {
               'title': title,
               'greetings': greetings,
               'form': form,
    }
    return render_to_response(template_name, context,
            context_instance=RequestContext(request))


на которой выводится список добавленных ранее приветствий и форма для добавления нового приветствия.



Создание теста Selenium


Запустим проект и в браузере создадим тест с помощью плагина, смотрите на видео (советую смотреть в HQ):



Как видите, у нас получился код на питоне. Из этого сгенерированного кода возьмём файл класса и немножко доработаем его, чтобы в нашем tests.py получилось вот такое:

# coding: utf-8
 
from djangosanetesting.selenium.driver import selenium
import unittest, time, re
 
class Untitled(unittest.TestCase):
 
    test_type = «selenium»
    start_live_server = True
 
    def setUp(self):
        self.start_live_server=True
        self.verificationErrors = []
        self.selenium = selenium(«localhost», 4444, "*chrome", «127.0.0.1:8000/»)
        self.selenium.start()
 
    def test_required_text_exists(self):
        sel = self.selenium
        sel.open("/")
        sel.type(«id_text», «Testing with Selenium»)
        sel.click(u"//input[@value='Добавить']")
        sel.wait_for_page_to_load(«30000»)
        try: self.failUnless(sel.is_text_present(«Testing with Selenium»))
        except AssertionError, e: self.verificationErrors.append(str(e))
 
    def tearDown(self):
        self.selenium.stop()
        self.assertEqual([], self.verificationErrors)
 
if __name__ == "__main__":
    unittest.main()


В класс мы добавили два атрибута. test_type — указывает на тип теста и start_live_server — он говорит о том, что нужно будет запускать тестовый сервер.

Кроме того, импорты другие, после установки sane у нас уже всё есть в установленном пакете.



Скрипты для запуска тестов


Есть два варианта настроек для запуска тестов. Первый вариант — добавить в settings.py переменную TEST_RUNNER, но в этом случае не получится выбирать индивидуальные типы тестов, например, только юнит-тесты, или только селениум-тесты, а ведь не всегда нужно запускать все.

Поэтому я выбрал второй вариант — написал скрипты для запуска тестов. Первый скрипт, nose-selenium:

#!/bin/bash
DJANGO_SETTINGS_MODULE=«settings» PYTHONPATH=".:.." nosetests --with-django --with-djangoliveserver --with-sanetestselection --select-seleniumtests;

этот скрипт запустит селениум-тесты с тестовым сервером.

Второй скрипт, nose-unittests:

#!/bin/bash
DJANGO_SETTINGS_MODULE=«settings» PYTHONPATH=".:.." nosetests --with-django --with-sanetestselection --select-unittests;

этот скрипт запустит юнит-тесты, без тестового сервера.

Можно сделать необходимые скрипты на все случаи жизни, все типы тестов перечислены в документации. Не забудьте установить права на выполнение скриптов, chmod 755.



Запуск тестов


Теперь всё готово для запуска тестов. Необходимо стартануть сервер Selenium, /usr/local/bin/selenium-server, после чего можно запускать тесты с помощью скриптов.

Я не стал делать скринкаст, но будьте уверены, что всё работает ОК — запускается браузер, контачит с сервером, заполняет форму, проверяет наличие текста, результаты выводятся как обычно.

Код к статье выложил на яндексе, если файл оттуда исчезнет, пишите в личку.



Резюме


На мой взгляд, селениум является хорошим дополнением к системе тестов, т.к. позволяет тестить javascript-насыщенные интерфейсы, что стандартной системе тестов не под силу. Кроме того, сама по себе Django: Sane Testing предлагает дополнительные фичи, о которых я вам советую прочесть в документации.

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



P.S.


Windmill не пробовал. Если она проще\лучше\надёжнее, чем Selenium, пожалуйста опишите в комментах или в виде статьи. Линк на статью помещу в начало этой.
Теги:
Хабы:
+28
Комментарии 16
Комментарии Комментарии 16

Публикации

Истории

Работа

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

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн
Геймтон «DatsEdenSpace» от DatsTeam
Дата 5 – 6 апреля
Время 17:00 – 20:00
Место
Онлайн