SlideShare a Scribd company logo
Разработка через
  тестирование
в Python и Django
             Илья Шаляпин
         Евгений Генералов
19 проектов
    4 года
89299 строк кода
50826 строк тестов
Писать тесты или нет?
Пример из жизни
Переезд с Ubuntu 8.04 на Ubuntu 12.04

Python 2.5                 Python 2.7
Django 1.3                 Django 1.4.0
lxml 1.3.6                 lxml 2.3.2
PIL 1.1.6                  PIL 1.1.7
...                        ...
Перезд проекта плотно
  покрытого тестами
Перезд проекта менее плотно
    покрытого тестами
Перезд проекта без тестов
Преимущества


- Меньше ручной работы
- Спокойный рефакторинг
- Код легче читать
- Быстрое подключение людей к проекту
- Тесты являются спецификацией
Недостатки


- Затраты на обучение
- Дополнительные настроки в проекте
- Некоторые тесты сложно писать
TDD вид сбоку
$ pip install unittest2
# test_add.py

import unittest2


class AddTest(unittest2.TestCase):

  def test_add(self):
    self.assertEquals(add(1, 1), 2)
    self.assertEquals(add(5, 2), 7)
    self.assertEquals(add(-1, -6), -7)


if __name__ == '__main__':
    unittest2.main()
# test_add.py

import unittest2

def add(a, b):
   pass


class AddTest(unittest2.TestCase):

  def test_add(self):
    self.assertEquals(add(1, 1), 2)


if __name__ == '__main__':
    unittest2.main()
Запуск теста




$ python test_add.py
$ python test_add.py
F
=========================================
FAIL: test_add (__main__.AddTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_add.py", line 11, in test_add
   self.assertEquals(add(1, 1), 2)
AssertionError: None != 2

----------------------------------------------------------------------
Ran 1 test in 0.000s

FAILED (failures=1)
# test_add.py

import unittest2

def add(a, b):
  return a + b


class AddTest(unittest2.TestCase):

  def test_add(self):
    self.assertEquals(add(1, 1), 2)


if __name__ == '__main__':
    unittest2.main()
$ python test_add.py
.
-------------------------------------------------
Ran 1 test in 0.000s

OK
Проект растет - тестов
  становится много

 ...
 ./tests/
 ./tests/test_add.py
 ./tests/test_sub.py
 ./tests/test_div.py
 ./tests/test_mul.py
 ./tests/test_pi.py
Nose - запускалка тестов
            Устанавливаем nose
$ pip install nose


               Запускаем тесты
$ nosetests
..
--------------------------------------------
Ran 100500 tests in 0.219s

OK
Инструменты




unittest2       django.test
flexmock        django_nose
nose            django_webtest
Тестирование в Django

Установить приложения
   $ pip install django_nose
   $ pip install django_webtest


Создать тестовую конфигурацию
    testing_settings.py
# testing_settings.py
from settings import *

DATABASES = {
  "default": dict(
    ENGINE = "django.db.backends.sqlite3",
    NAME = ":memory:",
  )
}

INSTALLED_APPS += (
  'django_nose',
)

TEST_RUNNER = 'django_nose.
NoseTestSuiteRunner'
Запуск тестов в Django

Запуск всех тестов в папке ./blog

$ manage.py test ./blog --settings project.
testing_settings



Запуск тестов в одном файле

$ manage.py test ./blog/test/test_forms.py --settings
project.testing_settings
Запуск тестов только для одного класса

$ manage.py test ./blog/test/test_forms.py:PostFormTest
--settings project.testing_settings



Запуск только одного теста

$ manage.py test ./blog/test/test_forms.py:PostFormTest.
test_post_from_submit --settings project.testing_settings
Blog tutorial
Тест view

from django.test import TestCase, Client


class HomePageTest(TestCase):

  def test_homepage_is_available(self):
    c = Client()
    response = c.get('/')
    self.assertEquals(response.status_code, 200)
class HomePageTest(TestCase):

  def setUp(self):
    self.posts = [ ]
    for i in range(20):
           post = Post.objects.create(
                 title = "Hello %d" % i,
           )
           self.posts.append(post)

  def test_homepage_contains_posts(self):
    pass
class HomePageTest(TestCase):

  def setUp(self):
    self.posts = [ ]
    for i in range(20):
           post = Post.objects.create(
                 title = "Hello %d" % i,
           )
           self.posts.append(post)

  def test_homepage_contains_posts(self):
    c = Client()
    response = c.get('/')
    self.assertEquals(response.status_code, 200)
    self.assertIn(self.posts[-1].title, response.content)
    self.assertIn(self.posts[-2].title, response.content)
class HomePageTest(TestCase):

  def setUp(self):
    pass

  def tearDown(self):
    pass

  def test_homepage_contains_posts(self):
    pass
def home(request):
  posts = Post.objects.all()[:10]
  return render(request, 'home.html', {'posts':posts})
from django.db import models


class Post(models.Model):
   picture = models.ImageField(
       upload_to='posts', blank=True, null=True)
   title = models.CharField(max_length=255)
   body = models.CharField(max_length=255)

  class Meta:
     ordering = ['-id']
Отправка формы

class PostFormTest(TestCase):

  def test_post_from_submit(self):
    c = Client()
    params = {'title':'Hello Pycon'}
    response = c.post('/posts/add/', params)
    self.assertEquals(response.status_code, 302)
    post = Post.objects.get(title=params['title'])
Загрузка файлов

def test_post_from_submit_with_picture(self):
  f = open('blog/tests/fixtures/debian-logo.png')
  params = {
      'picture':f,
      'title':'My photo',
  }
  response = self.client.post('/posts/add/', params)
  self.assertEquals(response.status_code, 302)
  post = Post.objects.get(title=params['title'])
  self.assertIn('.png', post.picture.path)
$ pip install django_webtest
django_webtest - XPath
class HomePageWebTest(WebTest):

  def setUp(self):
    ...

  def test_homepage_contains_posts(self):
    response = self.app.get('/')
    self.assertEquals(response.status_int, 200)
    titles = response.lxml.xpath(
         "//*[@class='post-announce']/h2/text()"
    )
    self.assertEquals(titles[0], self.posts[-1].title)
    self.assertEquals(titles[1], self.posts[-2].title)
django_webtest - формы

from django_webtest import WebTest

class PostFormWebTest(WebTest):

  def test_post_from_submit(self):
    response = self.app.get('/posts/add/')
    self.assertEquals(response.status_int, 200)
    form = response.forms['add_post_form']
    form['title'] = 'Hello Pycon'
    form['body'] = 'Wazzup!'
    response = form.submit().follow()
    self.assertEquals(response.status_int, 200)
Тесты админки




Почти такие же как тесты других view
class PostAdminTest(TestCase):

  def setUp(self):
    self.user = User.objects.create_user(
       'admin',
       'mail@example.com',
       'password'
    )
    self.user.is_staff = True
    self.user.is_superuser = True
    self.user.save()

  def test_post_form_submit(self):
    ...
class PostAdminTest(TestCase):

  def setUp(self):
    ...

  def test_post_form_submit(self):
    c = Client()
    c.login(username='admin', password='password')
    response = c.get('/admin/blog/post/add/')
    self.assertEquals(response.status_code, 200)
    params = {'title': 'Hello Pycon', 'body': 'Text'}
    response = c.post('/posts/add/', params)
    self.assertEquals(response.status_code, 302)
    post = Post.objects.get(title=params['title'])
Прочее в Django

- Middleware
- Template tags, filters
- Context processors


- тестируются модульными тестами как
простые функции, аналогично с
примером 1+1 = 2
Особенности тестов view в Django

     ----------------------------
     middleware
     -----------------------------
     context processors
     -----------------------------
     template
     -----------------------------
     view
     -----------------------------
     models
     -----------------------------
     network
Flexmock

- Заменять части объектов и классов
- Заменять функции, в том числе
встроенные
- Создавать объекты заглушки
- Проверять ожидания (сколько раз
вызван метод, с какими аргументами)
$ pip install flexmock
from flexmock import flexmock
from blog.models import Post

def test_home_page_with_flexmock(self):
  posts = [
     Post(title='hello flexmock'),
     Post(title='hello flexmock'),
  ]
  (flexmock(Post.objects)
     .should_receive('all')
     .and_return(posts)
     .once())
  response = self.client.get('/')
  self.assertEquals(response.status_code, 200)
  self.assertIn('hello flexmock', response.content)
from flexmock import flexmock
import blog.views

def test_home_view_as_unittest(self):
  request = flexmock(
     GET={},
     POST={},
     META={'HTTP_HOST':'example.com'}
  )
  response = blog.views.home(request)
  self.assertEquals(response.status_code, 200)
Теория vs практика
Есть требования ...

def get_url_content(url):
  # ToDo
  # Вернуть контент страницы
  # или None, в случае ошибки
  pass
Как написать тест?



def test_get_url_content(self):
  url = 'http://example.com'
  text = get_url_content(url)
  self.assertEquals(text, ???)
Тестирование реализации
Пишем тест имея представление о внутренностях

def get_url_content(url):
  try:
     response = urllib.urlopen(url)
     content = response.read()
     response.close()
  except IOError:
     return None
  return content


Неверно с точки зрения теории,
удобно на практике
Тест для случая нормального
        выполнения
 def test_get_url_content(self):
   url = 'http://example.com'
   response = StringIO("<html>")
   (flexmock(urllib)
      .should_receive('urlopen')
      .with_args(url)
      .and_return(response)
      .once())
   text = get_url_content(url)
   self.assertEquals(text, "<html>")
Тест в случае ошибки сети

def test_get_url_content_on_ioerror(self):
  url = 'http://example.com'
  (flexmock(urllib)
     .should_receive('urlopen')
     .with_args(url)
     .and_raise(IOError("test exception"))
     .once())
  text = get_url_content(url)
  self.assertEquals(text, None)
Примеры тестов



https://bitbucket.org/ishalyapin/python-test-examples

https://bitbucket.org/ishalyapin/django-test-examples
Спасибо за внимание!
  Доклад подготовили

 Илья Шаляпин
 ishalyapin@gmail.com
 www.ishalyapin.ru
 www.bookradar.org
 bitbucket.org/ishalyapin
 github.com/un1t



 Евгений Генералов
 e.generalov@gmail.com
 github.com/generalov

More Related Content

PDF
Pycon Russia 2013 - Разработка через тестирование в Python и Django
PDF
Учим автотесты человеческому языку с помощью Allure и PyTest
PPTX
Разработка расширяемых приложений на Django
PPTX
kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно
ODP
Unit test быстрый старт
PDF
Лекция 7. Исключения и менеджеры контекста.
PPTX
XPath локаторы в Selenium WebDriver
PPTX
Selenium: начало работы
Pycon Russia 2013 - Разработка через тестирование в Python и Django
Учим автотесты человеческому языку с помощью Allure и PyTest
Разработка расширяемых приложений на Django
kranonitS20 Сергей Бурма. Django - легко, быстро, эффективно
Unit test быстрый старт
Лекция 7. Исключения и менеджеры контекста.
XPath локаторы в Selenium WebDriver
Selenium: начало работы

What's hot (19)

PPT
бегун
PPT
ZFConf 2010: What News Zend Framework 2.0 Brings to Us
PPTX
Web осень 2013 лекция 7
PDF
Unit testing iOS Applications
PDF
Zend Framework и Doctrine
PPTX
Web осень 2013 лекция 8
PDF
Нескучное тестирование с pytest
PDF
C# Desktop. Занятие 12.
PPT
Dependency injection, phemto
PPTX
Web осень 2013 лекция 4
PDF
Лекция 6. Классы 1.
PPTX
Максим Щепелин. "Unittesting. Как?"
PDF
Лекция #5. Введение в язык программирования Python 3
PDF
Лекция 5. Встроенные коллекции и модуль collections.
PPTX
Selenium vs AJAX
PDF
Javascript testing
PPT
Alexander manuhin selenium_php_v2.0
PDF
Angular 2: Всех переиграл
PPTX
бегун
ZFConf 2010: What News Zend Framework 2.0 Brings to Us
Web осень 2013 лекция 7
Unit testing iOS Applications
Zend Framework и Doctrine
Web осень 2013 лекция 8
Нескучное тестирование с pytest
C# Desktop. Занятие 12.
Dependency injection, phemto
Web осень 2013 лекция 4
Лекция 6. Классы 1.
Максим Щепелин. "Unittesting. Как?"
Лекция #5. Введение в язык программирования Python 3
Лекция 5. Встроенные коллекции и модуль collections.
Selenium vs AJAX
Javascript testing
Alexander manuhin selenium_php_v2.0
Angular 2: Всех переиграл
Ad

Viewers also liked (17)

PDF
Алексей Кулаков: Доклад о ненависти к рыбе...и любви к осмысленному тексту
PPT
Разработка сайта как создание мультфильма #oseminar
PDF
Илья Седов: Как заставить программу под iOS шевелиться? #uwdc
PDF
Алексей Кулаков: Теория ограничений Голдратта #oseminar
PDF
Минисервисы или микросервисы в условия цейтнота, Руслан Каримов, UWDC 2015
PDF
Как выжить в условиях сложного продукта. Дарья Прокуда.
PDF
Битва мух и котлет. Сторителлинг в проектировании взаимодействия.
PDF
Об особенностях работы художника, который гипотетически может все
PDF
Исследовательские проекты в жизненном цикле и рутинных процессах компании
PDF
Автоматизация общения с менеджером, Полина Бынова, JetStyle
PDF
Данные на службе бизнеса
PPTX
CPC-трафик в unit-экономике, Полина Бынова
PPT
Сергей Беляев: Интернет-телевизор - самый недооцененный сегмент разработки #uwdc
PDF
Управление - это игра. Алексей Кулаков, JetStyle
PDF
MVP (минимальный жизнеспособный продукт): как не потерять деньги на разработк...
PDF
Создание нового стиля, дизайна и шрифта для "Батенька, да вы трансформер!"
PDF
Разработка MVP. Зачем это нужно и как это делать?
Алексей Кулаков: Доклад о ненависти к рыбе...и любви к осмысленному тексту
Разработка сайта как создание мультфильма #oseminar
Илья Седов: Как заставить программу под iOS шевелиться? #uwdc
Алексей Кулаков: Теория ограничений Голдратта #oseminar
Минисервисы или микросервисы в условия цейтнота, Руслан Каримов, UWDC 2015
Как выжить в условиях сложного продукта. Дарья Прокуда.
Битва мух и котлет. Сторителлинг в проектировании взаимодействия.
Об особенностях работы художника, который гипотетически может все
Исследовательские проекты в жизненном цикле и рутинных процессах компании
Автоматизация общения с менеджером, Полина Бынова, JetStyle
Данные на службе бизнеса
CPC-трафик в unit-экономике, Полина Бынова
Сергей Беляев: Интернет-телевизор - самый недооцененный сегмент разработки #uwdc
Управление - это игра. Алексей Кулаков, JetStyle
MVP (минимальный жизнеспособный продукт): как не потерять деньги на разработк...
Создание нового стиля, дизайна и шрифта для "Батенька, да вы трансформер!"
Разработка MVP. Зачем это нужно и как это делать?
Ad

Similar to Разработка через тестирование в Python и Django #pyconru (20)

PPT
Web весна 2012 лекция 7
PDF
"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone
PDF
12 - Web-технологии. Django модели
PPTX
Magento code debugging
PPTX
Meet Magento Belarus debug Pavel Novitsky (rus)
PPTX
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
PPT
TestGuy - эмулируем вашего тестировщика
PPT
Django South. Миграция баз данных.
PPT
бегун
PDF
Looking into WordPress Core, WordCamp Russia 2015
PPTX
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
PDF
Первые шаги после установки WordPress
PDF
Mobile automation uamobile
PPT
Производительность в Django
PDF
TestRail. Некоторые возможности интеграции.
PPTX
iOS and Android Mobile Test Automation
PDF
Дело тестера боится: как в опытных руках могут заиграть Java и TestNg
PPTX
Python Meetup
PDF
10 - Web-технологии. MVC фреймворки (продолжение)
PDF
Erlang tasty & useful stuff
Web весна 2012 лекция 7
"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone
12 - Web-технологии. Django модели
Magento code debugging
Meet Magento Belarus debug Pavel Novitsky (rus)
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
TestGuy - эмулируем вашего тестировщика
Django South. Миграция баз данных.
бегун
Looking into WordPress Core, WordCamp Russia 2015
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
Первые шаги после установки WordPress
Mobile automation uamobile
Производительность в Django
TestRail. Некоторые возможности интеграции.
iOS and Android Mobile Test Automation
Дело тестера боится: как в опытных руках могут заиграть Java и TestNg
Python Meetup
10 - Web-технологии. MVC фреймворки (продолжение)
Erlang tasty & useful stuff

More from JetStyle (14)

PDF
Нарративные игры как метод прототипирования, Алексей Кулаков, UWDC 2015
PDF
Постановка задачи на сайт. Алексей Кулаков
PDF
Как узнать о ваших покупателях все?
PPTX
Петли в проектировании интерфейсов, DUMP 2014
PPTX
Как создать зарабатывающее приложение, UIN 2014
PDF
Frontttalks nov2013
PPT
DUMP-2013 - Frontend: Преимущества разработки средствами BEM+Python+node.js
PPT
Мобильное приложение для бизнеса: взгляд со стороны разработчика
PPTX
Интерфейсы для Smart TV #uxsreda
PDF
Алексей Кулаков: Суеверия vs научный подход в проектировании интерфейсов #uwdc
PPTX
Борис Касавин: Big data для нужд реального российского бизнеса #uwdc
PDF
Алексей Иванов: немного о grunt.js #FrontTalks
PPTX
Александр Устинов: Работа с существующими клиентами (бизнес-завтрак)
PPT
Сергей Наймушин: Управление привлечением новых клиентов (бизнес-завтрак)
Нарративные игры как метод прототипирования, Алексей Кулаков, UWDC 2015
Постановка задачи на сайт. Алексей Кулаков
Как узнать о ваших покупателях все?
Петли в проектировании интерфейсов, DUMP 2014
Как создать зарабатывающее приложение, UIN 2014
Frontttalks nov2013
DUMP-2013 - Frontend: Преимущества разработки средствами BEM+Python+node.js
Мобильное приложение для бизнеса: взгляд со стороны разработчика
Интерфейсы для Smart TV #uxsreda
Алексей Кулаков: Суеверия vs научный подход в проектировании интерфейсов #uwdc
Борис Касавин: Big data для нужд реального российского бизнеса #uwdc
Алексей Иванов: немного о grunt.js #FrontTalks
Александр Устинов: Работа с существующими клиентами (бизнес-завтрак)
Сергей Наймушин: Управление привлечением новых клиентов (бизнес-завтрак)

Разработка через тестирование в Python и Django #pyconru

  • 1. Разработка через тестирование в Python и Django Илья Шаляпин Евгений Генералов
  • 2. 19 проектов 4 года 89299 строк кода 50826 строк тестов
  • 4. Пример из жизни Переезд с Ubuntu 8.04 на Ubuntu 12.04 Python 2.5 Python 2.7 Django 1.3 Django 1.4.0 lxml 1.3.6 lxml 2.3.2 PIL 1.1.6 PIL 1.1.7 ... ...
  • 5. Перезд проекта плотно покрытого тестами
  • 6. Перезд проекта менее плотно покрытого тестами
  • 8. Преимущества - Меньше ручной работы - Спокойный рефакторинг - Код легче читать - Быстрое подключение людей к проекту - Тесты являются спецификацией
  • 9. Недостатки - Затраты на обучение - Дополнительные настроки в проекте - Некоторые тесты сложно писать
  • 11. $ pip install unittest2
  • 12. # test_add.py import unittest2 class AddTest(unittest2.TestCase): def test_add(self): self.assertEquals(add(1, 1), 2) self.assertEquals(add(5, 2), 7) self.assertEquals(add(-1, -6), -7) if __name__ == '__main__': unittest2.main()
  • 13. # test_add.py import unittest2 def add(a, b): pass class AddTest(unittest2.TestCase): def test_add(self): self.assertEquals(add(1, 1), 2) if __name__ == '__main__': unittest2.main()
  • 15. $ python test_add.py F ========================================= FAIL: test_add (__main__.AddTest) ---------------------------------------------------------------------- Traceback (most recent call last): File "test_add.py", line 11, in test_add self.assertEquals(add(1, 1), 2) AssertionError: None != 2 ---------------------------------------------------------------------- Ran 1 test in 0.000s FAILED (failures=1)
  • 16. # test_add.py import unittest2 def add(a, b): return a + b class AddTest(unittest2.TestCase): def test_add(self): self.assertEquals(add(1, 1), 2) if __name__ == '__main__': unittest2.main()
  • 18. Проект растет - тестов становится много ... ./tests/ ./tests/test_add.py ./tests/test_sub.py ./tests/test_div.py ./tests/test_mul.py ./tests/test_pi.py
  • 19. Nose - запускалка тестов Устанавливаем nose $ pip install nose Запускаем тесты $ nosetests .. -------------------------------------------- Ran 100500 tests in 0.219s OK
  • 20. Инструменты unittest2 django.test flexmock django_nose nose django_webtest
  • 21. Тестирование в Django Установить приложения $ pip install django_nose $ pip install django_webtest Создать тестовую конфигурацию testing_settings.py
  • 22. # testing_settings.py from settings import * DATABASES = { "default": dict( ENGINE = "django.db.backends.sqlite3", NAME = ":memory:", ) } INSTALLED_APPS += ( 'django_nose', ) TEST_RUNNER = 'django_nose. NoseTestSuiteRunner'
  • 23. Запуск тестов в Django Запуск всех тестов в папке ./blog $ manage.py test ./blog --settings project. testing_settings Запуск тестов в одном файле $ manage.py test ./blog/test/test_forms.py --settings project.testing_settings
  • 24. Запуск тестов только для одного класса $ manage.py test ./blog/test/test_forms.py:PostFormTest --settings project.testing_settings Запуск только одного теста $ manage.py test ./blog/test/test_forms.py:PostFormTest. test_post_from_submit --settings project.testing_settings
  • 26. Тест view from django.test import TestCase, Client class HomePageTest(TestCase): def test_homepage_is_available(self): c = Client() response = c.get('/') self.assertEquals(response.status_code, 200)
  • 27. class HomePageTest(TestCase): def setUp(self): self.posts = [ ] for i in range(20): post = Post.objects.create( title = "Hello %d" % i, ) self.posts.append(post) def test_homepage_contains_posts(self): pass
  • 28. class HomePageTest(TestCase): def setUp(self): self.posts = [ ] for i in range(20): post = Post.objects.create( title = "Hello %d" % i, ) self.posts.append(post) def test_homepage_contains_posts(self): c = Client() response = c.get('/') self.assertEquals(response.status_code, 200) self.assertIn(self.posts[-1].title, response.content) self.assertIn(self.posts[-2].title, response.content)
  • 29. class HomePageTest(TestCase): def setUp(self): pass def tearDown(self): pass def test_homepage_contains_posts(self): pass
  • 30. def home(request): posts = Post.objects.all()[:10] return render(request, 'home.html', {'posts':posts})
  • 31. from django.db import models class Post(models.Model): picture = models.ImageField( upload_to='posts', blank=True, null=True) title = models.CharField(max_length=255) body = models.CharField(max_length=255) class Meta: ordering = ['-id']
  • 32. Отправка формы class PostFormTest(TestCase): def test_post_from_submit(self): c = Client() params = {'title':'Hello Pycon'} response = c.post('/posts/add/', params) self.assertEquals(response.status_code, 302) post = Post.objects.get(title=params['title'])
  • 33. Загрузка файлов def test_post_from_submit_with_picture(self): f = open('blog/tests/fixtures/debian-logo.png') params = { 'picture':f, 'title':'My photo', } response = self.client.post('/posts/add/', params) self.assertEquals(response.status_code, 302) post = Post.objects.get(title=params['title']) self.assertIn('.png', post.picture.path)
  • 34. $ pip install django_webtest
  • 35. django_webtest - XPath class HomePageWebTest(WebTest): def setUp(self): ... def test_homepage_contains_posts(self): response = self.app.get('/') self.assertEquals(response.status_int, 200) titles = response.lxml.xpath( "//*[@class='post-announce']/h2/text()" ) self.assertEquals(titles[0], self.posts[-1].title) self.assertEquals(titles[1], self.posts[-2].title)
  • 36. django_webtest - формы from django_webtest import WebTest class PostFormWebTest(WebTest): def test_post_from_submit(self): response = self.app.get('/posts/add/') self.assertEquals(response.status_int, 200) form = response.forms['add_post_form'] form['title'] = 'Hello Pycon' form['body'] = 'Wazzup!' response = form.submit().follow() self.assertEquals(response.status_int, 200)
  • 37. Тесты админки Почти такие же как тесты других view
  • 38. class PostAdminTest(TestCase): def setUp(self): self.user = User.objects.create_user( 'admin', 'mail@example.com', 'password' ) self.user.is_staff = True self.user.is_superuser = True self.user.save() def test_post_form_submit(self): ...
  • 39. class PostAdminTest(TestCase): def setUp(self): ... def test_post_form_submit(self): c = Client() c.login(username='admin', password='password') response = c.get('/admin/blog/post/add/') self.assertEquals(response.status_code, 200) params = {'title': 'Hello Pycon', 'body': 'Text'} response = c.post('/posts/add/', params) self.assertEquals(response.status_code, 302) post = Post.objects.get(title=params['title'])
  • 40. Прочее в Django - Middleware - Template tags, filters - Context processors - тестируются модульными тестами как простые функции, аналогично с примером 1+1 = 2
  • 41. Особенности тестов view в Django ---------------------------- middleware ----------------------------- context processors ----------------------------- template ----------------------------- view ----------------------------- models ----------------------------- network
  • 42. Flexmock - Заменять части объектов и классов - Заменять функции, в том числе встроенные - Создавать объекты заглушки - Проверять ожидания (сколько раз вызван метод, с какими аргументами)
  • 43. $ pip install flexmock
  • 44. from flexmock import flexmock from blog.models import Post def test_home_page_with_flexmock(self): posts = [ Post(title='hello flexmock'), Post(title='hello flexmock'), ] (flexmock(Post.objects) .should_receive('all') .and_return(posts) .once()) response = self.client.get('/') self.assertEquals(response.status_code, 200) self.assertIn('hello flexmock', response.content)
  • 45. from flexmock import flexmock import blog.views def test_home_view_as_unittest(self): request = flexmock( GET={}, POST={}, META={'HTTP_HOST':'example.com'} ) response = blog.views.home(request) self.assertEquals(response.status_code, 200)
  • 47. Есть требования ... def get_url_content(url): # ToDo # Вернуть контент страницы # или None, в случае ошибки pass
  • 48. Как написать тест? def test_get_url_content(self): url = 'http://example.com' text = get_url_content(url) self.assertEquals(text, ???)
  • 49. Тестирование реализации Пишем тест имея представление о внутренностях def get_url_content(url): try: response = urllib.urlopen(url) content = response.read() response.close() except IOError: return None return content Неверно с точки зрения теории, удобно на практике
  • 50. Тест для случая нормального выполнения def test_get_url_content(self): url = 'http://example.com' response = StringIO("<html>") (flexmock(urllib) .should_receive('urlopen') .with_args(url) .and_return(response) .once()) text = get_url_content(url) self.assertEquals(text, "<html>")
  • 51. Тест в случае ошибки сети def test_get_url_content_on_ioerror(self): url = 'http://example.com' (flexmock(urllib) .should_receive('urlopen') .with_args(url) .and_raise(IOError("test exception")) .once()) text = get_url_content(url) self.assertEquals(text, None)
  • 53. Спасибо за внимание! Доклад подготовили Илья Шаляпин ishalyapin@gmail.com www.ishalyapin.ru www.bookradar.org bitbucket.org/ishalyapin github.com/un1t Евгений Генералов e.generalov@gmail.com github.com/generalov