ݺߣ

ݺߣShare 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

What's hot (19)

Web осень 2013 лекция 7
Web осень 2013 лекция 7Web осень 2013 лекция 7
Web осень 2013 лекция 7
Technopark
Selenium: начало работы
Selenium: начало работыSelenium: начало работы
Selenium: начало работы
Paul Stashevsky
Unit testing iOS Applications
Unit testing iOS ApplicationsUnit testing iOS Applications
Unit testing iOS Applications
Andrey Volobuev
Web осень 2013 лекция 8
Web осень 2013 лекция 8Web осень 2013 лекция 8
Web осень 2013 лекция 8
Technopark
C# Desktop. Занятие 12.
C# Desktop. Занятие 12.C# Desktop. Занятие 12.
C# Desktop. Занятие 12.
Igor Shkulipa
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
Technopark
Нескучное тестирование с pytest
Нескучное тестирование с pytestНескучное тестирование с pytest
Нескучное тестирование с pytest
Roman Imankulov
Web осень 2013 лекция 4
Web осень 2013 лекция 4Web осень 2013 лекция 4
Web осень 2013 лекция 4
Technopark
Dependency injection, phemto
Dependency injection, phemtoDependency injection, phemto
Dependency injection, phemto
Vladimir Romanitchev
"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone
"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone
"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone
it-people
Angular 2: Всех переиграл
Angular 2: Всех переигралAngular 2: Всех переиграл
Angular 2: Всех переиграл
Eugene Zharkov
Лекция 6. Классы 1.
Лекция 6. Классы 1.Лекция 6. Классы 1.
Лекция 6. Классы 1.
Roman Brovko
Drf vs Graphql
Drf vs GraphqlDrf vs Graphql
Drf vs Graphql
Вадим Шевченко
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.
Roman Brovko
Web осень 2013 лекция 9
Web осень 2013 лекция 9Web осень 2013 лекция 9
Web осень 2013 лекция 9
Technopark
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
Яковенко Кирилл
Web осень 2013 лекция 5
Web осень 2013 лекция 5Web осень 2013 лекция 5
Web осень 2013 лекция 5
Technopark
Web осень 2013 лекция 7
Web осень 2013 лекция 7Web осень 2013 лекция 7
Web осень 2013 лекция 7
Technopark
Selenium: начало работы
Selenium: начало работыSelenium: начало работы
Selenium: начало работы
Paul Stashevsky
Web осень 2013 лекция 8
Web осень 2013 лекция 8Web осень 2013 лекция 8
Web осень 2013 лекция 8
Technopark
C# Desktop. Занятие 12.
C# Desktop. Занятие 12.C# Desktop. Занятие 12.
C# Desktop. Занятие 12.
Igor Shkulipa
Web осень 2013 лекция 6
Web осень 2013 лекция 6Web осень 2013 лекция 6
Web осень 2013 лекция 6
Technopark
Нескучное тестирование с pytest
Нескучное тестирование с pytestНескучное тестирование с pytest
Нескучное тестирование с pytest
Roman Imankulov
Web осень 2013 лекция 4
Web осень 2013 лекция 4Web осень 2013 лекция 4
Web осень 2013 лекция 4
Technopark
"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone
"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone
"VUE.JS как реакт с человеческим лицом" Дулецкий Вольдэмар, Evrone
it-people
Angular 2: Всех переиграл
Angular 2: Всех переигралAngular 2: Всех переиграл
Angular 2: Всех переиграл
Eugene Zharkov
Лекция 6. Классы 1.
Лекция 6. Классы 1.Лекция 6. Классы 1.
Лекция 6. Классы 1.
Roman Brovko
Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.Лекция 5. Встроенные коллекции и модуль collections.
Лекция 5. Встроенные коллекции и модуль collections.
Roman Brovko
Web осень 2013 лекция 9
Web осень 2013 лекция 9Web осень 2013 лекция 9
Web осень 2013 лекция 9
Technopark
Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3Лекция #5. Введение в язык программирования Python 3
Лекция #5. Введение в язык программирования Python 3
Яковенко Кирилл
Web осень 2013 лекция 5
Web осень 2013 лекция 5Web осень 2013 лекция 5
Web осень 2013 лекция 5
Technopark

Similar to Илья Шаляпин, Евгений Генералов: Разработка через тестирование в Python и Djnago на практике (20)

Web весна 2012 лекция 7
Web весна 2012 лекция 7Web весна 2012 лекция 7
Web весна 2012 лекция 7
Technopark
12 - Web-технологии. Django модели
12 - Web-технологии. Django модели12 - Web-технологии. Django модели
12 - Web-технологии. Django модели
Roman Brovko
Magento code debugging
Magento code debuggingMagento code debugging
Magento code debugging
aheadWorks
Meet Magento Belarus debug Pavel Novitsky (rus)
Meet Magento Belarus debug Pavel Novitsky (rus)Meet Magento Belarus debug Pavel Novitsky (rus)
Meet Magento Belarus debug Pavel Novitsky (rus)
Pavel Novitsky
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Andrey Rebrov
TestGuy - эмулируем вашего тестировщика
TestGuy - эмулируем вашего тестировщикаTestGuy - эмулируем вашего тестировщика
TestGuy - эмулируем вашего тестировщика
davertmik
Django South. Миграция баз данных.
Django South. Миграция баз данных.  Django South. Миграция баз данных.
Django South. Миграция баз данных.
MoscowDjango
Looking into WordPress Core, WordCamp Russia 2015
Looking into WordPress Core, WordCamp Russia 2015Looking into WordPress Core, WordCamp Russia 2015
Looking into WordPress Core, WordCamp Russia 2015
Sergey Biryukov
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGвебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
Andrey Rebrov
Первые шаги после установки WordPress
Первые шаги после установки WordPressПервые шаги после установки WordPress
Первые шаги после установки WordPress
Darja Kruzhkova
Mobile automation uamobile
Mobile automation uamobileMobile automation uamobile
Mobile automation uamobile
UA Mobile
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
MoscowDjango
TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.
PyNSK
iOS and Android Mobile Test Automation
iOS and Android Mobile Test AutomationiOS and Android Mobile Test Automation
iOS and Android Mobile Test Automation
Andrii Dzynia
Дело тестера боится: как в опытных руках могут заиграть Java и TestNg
Дело тестера боится: как в опытных руках могут заиграть Java и TestNgДело тестера боится: как в опытных руках могут заиграть Java и TestNg
Дело тестера боится: как в опытных руках могут заиграть Java и TestNg
IT61
Python Meetup
Python Meetup Python Meetup
Python Meetup
iQSpace
10 - Web-технологии. MVC фреймворки (продолжение)
10 - Web-технологии. MVC фреймворки (продолжение)10 - Web-технологии. MVC фреймворки (продолжение)
10 - Web-технологии. MVC фреймворки (продолжение)
Roman Brovko
Инсталляционные профили, создание сборок
Инсталляционные профили, создание сборокИнсталляционные профили, создание сборок
Инсталляционные профили, создание сборок
Andrii Podanenko
Web весна 2012 лекция 7
Web весна 2012 лекция 7Web весна 2012 лекция 7
Web весна 2012 лекция 7
Technopark
12 - Web-технологии. Django модели
12 - Web-технологии. Django модели12 - Web-технологии. Django модели
12 - Web-технологии. Django модели
Roman Brovko
Magento code debugging
Magento code debuggingMagento code debugging
Magento code debugging
aheadWorks
Meet Magento Belarus debug Pavel Novitsky (rus)
Meet Magento Belarus debug Pavel Novitsky (rus)Meet Magento Belarus debug Pavel Novitsky (rus)
Meet Magento Belarus debug Pavel Novitsky (rus)
Pavel Novitsky
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Бодрящий микс из Selenium и TestNG- регрессионное тестирование руками разрабо...
Andrey Rebrov
TestGuy - эмулируем вашего тестировщика
TestGuy - эмулируем вашего тестировщикаTestGuy - эмулируем вашего тестировщика
TestGuy - эмулируем вашего тестировщика
davertmik
Django South. Миграция баз данных.
Django South. Миграция баз данных.  Django South. Миграция баз данных.
Django South. Миграция баз данных.
MoscowDjango
Looking into WordPress Core, WordCamp Russia 2015
Looking into WordPress Core, WordCamp Russia 2015Looking into WordPress Core, WordCamp Russia 2015
Looking into WordPress Core, WordCamp Russia 2015
Sergey Biryukov
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNGвебинар - функциональное тестирование с использованием Selenium 2 и TestNG
вебинар - функциональное тестирование с использованием Selenium 2 и TestNG
Andrey Rebrov
Первые шаги после установки WordPress
Первые шаги после установки WordPressПервые шаги после установки WordPress
Первые шаги после установки WordPress
Darja Kruzhkova
Mobile automation uamobile
Mobile automation uamobileMobile automation uamobile
Mobile automation uamobile
UA Mobile
Производительность в Django
Производительность в DjangoПроизводительность в Django
Производительность в Django
MoscowDjango
TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.TestRail. Некоторые возможности интеграции.
TestRail. Некоторые возможности интеграции.
PyNSK
iOS and Android Mobile Test Automation
iOS and Android Mobile Test AutomationiOS and Android Mobile Test Automation
iOS and Android Mobile Test Automation
Andrii Dzynia
Дело тестера боится: как в опытных руках могут заиграть Java и TestNg
Дело тестера боится: как в опытных руках могут заиграть Java и TestNgДело тестера боится: как в опытных руках могут заиграть Java и TestNg
Дело тестера боится: как в опытных руках могут заиграть Java и TestNg
IT61
Python Meetup
Python Meetup Python Meetup
Python Meetup
iQSpace
10 - Web-технологии. MVC фреймворки (продолжение)
10 - Web-технологии. MVC фреймворки (продолжение)10 - Web-технологии. MVC фреймворки (продолжение)
10 - Web-технологии. MVC фреймворки (продолжение)
Roman Brovko
Инсталляционные профили, создание сборок
Инсталляционные профили, создание сборокИнсталляционные профили, создание сборок
Инсталляционные профили, создание сборок
Andrii Podanenko

More from it-people (20)

«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co
«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co
«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co
it-people
«Scrapy internals» Александр Сибиряков, Scrapinghub
«Scrapy internals» Александр Сибиряков, Scrapinghub«Scrapy internals» Александр Сибиряков, Scrapinghub
«Scrapy internals» Александр Сибиряков, Scrapinghub
it-people
«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains
«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains
«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains
it-people
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
it-people
«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс
«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс
«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс
it-people
«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...
«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...
«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...
it-people
«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr
«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr
«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr
it-people
«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...
«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...
«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...
it-people
«Тотальный контроль производительности» Михаил Юматов, ЦИАН
«Тотальный контроль производительности» Михаил Юматов, ЦИАН«Тотальный контроль производительности» Михаил Юматов, ЦИАН
«Тотальный контроль производительности» Михаил Юматов, ЦИАН
it-people
«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк
«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк
«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк
it-people
«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co
«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co
«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co
it-people
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
it-people
«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...
«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...
«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...
it-people
«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies
«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies
«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies
it-people
«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System
«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System
«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System
it-people
«(Без)опасный Python», Иван Цыганов, Positive Technologies
«(Без)опасный Python», Иван Цыганов, Positive Technologies«(Без)опасный Python», Иван Цыганов, Positive Technologies
«(Без)опасный Python», Иван Цыганов, Positive Technologies
it-people
«Python of Things», Кирилл Борисов, Яндекс
«Python of Things», Кирилл Борисов, Яндекс«Python of Things», Кирилл Борисов, Яндекс
«Python of Things», Кирилл Борисов, Яндекс
it-people
«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...
«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...
«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...
it-people
«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician
«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician
«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician
it-people
«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...
«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...
«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...
it-people
«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co
«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co
«Про аналитику и серебряные пули» Александр Подсобляев, Rambler&Co
it-people
«Scrapy internals» Александр Сибиряков, Scrapinghub
«Scrapy internals» Александр Сибиряков, Scrapinghub«Scrapy internals» Александр Сибиряков, Scrapinghub
«Scrapy internals» Александр Сибиряков, Scrapinghub
it-people
«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains
«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains
«Отладка в Python 3.6: Быстрее, Выше, Сильнее» Елизавета Шашкова, JetBrains
it-people
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
«Gevent — быть или не быть?» Александр Мокров, Positive Technologies
it-people
«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс
«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс
«Ещё один Поиск Яндекса» Александр Кошелев, Яндекс
it-people
«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...
«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...
«How I Learned to Stop Worrying and Love the BFG: нагрузочное тестирование со...
it-people
«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr
«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr
«Write once run anywhere — почём опиум для народа?» Игорь Новиков, Scalr
it-people
«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...
«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...
«Gensim — тематическое моделирование для людей» Иван Меньших, Лев Константино...
it-people
«Тотальный контроль производительности» Михаил Юматов, ЦИАН
«Тотальный контроль производительности» Михаил Юматов, ЦИАН«Тотальный контроль производительности» Михаил Юматов, ЦИАН
«Тотальный контроль производительности» Михаил Юматов, ЦИАН
it-people
«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк
«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк
«Детские болезни live-чата» Ольга Сентемова, Тинькофф Банк
it-people
«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co
«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co
«Микросервисы наносят ответный удар!» Олег Чуркин, Rambler&Co
it-people
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
«Память и Python. Что надо знать для счастья?» Алексей Кузьмин, ЦНС
it-people
«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...
«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...
«Что такое serverless-архитектура и как с ней жить?» Николай Марков, Aligned ...
it-people
«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies
«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies
«Python на острие бритвы: PyPy project» Александр Кошкин, Positive Technologies
it-people
«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System
«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System
«PyWat. А хорошо ли вы знаете Python?» Александр Швец, Marilyn System
it-people
«(Без)опасный Python», Иван Цыганов, Positive Technologies
«(Без)опасный Python», Иван Цыганов, Positive Technologies«(Без)опасный Python», Иван Цыганов, Positive Technologies
«(Без)опасный Python», Иван Цыганов, Positive Technologies
it-people
«Python of Things», Кирилл Борисов, Яндекс
«Python of Things», Кирилл Борисов, Яндекс«Python of Things», Кирилл Борисов, Яндекс
«Python of Things», Кирилл Борисов, Яндекс
it-people
«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...
«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...
«Как сделать так, чтобы тесты на Swift не причиняли боль» Сычев Александр, Ra...
it-people
«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician
«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician
«Клиенту и серверу нужно поговорить» Прокопов Никита, Cognician
it-people
«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...
«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...
«Кошелек или деньги: сложный выбор между памятью и процессором» Алексеенко Иг...
it-people

Илья Шаляпин, Евгений Генералов: Разработка через тестирование в Python и Djnago на практике

  • 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