Your SlideShare is downloading. ×
0
Разработка через  тестированиев Python и Django             Илья Шаляпин         Евгений Генералов
19 проектов    4 года89299 строк кода50826 строк тестов
Писать тесты или нет?
Пример из жизниПереезд с Ubuntu 8.04 на Ubuntu 12.04Python 2.5                 Python 2.7Django 1.3                 Django...
Перезд проекта плотно  покрытого тестами
Перезд проекта менее плотно    покрытого тестами
Перезд проекта без тестов
Преимущества- Меньше ручной работы- Спокойный рефакторинг- Код легче читать- Быстрое подключение людей к проекту- Тесты яв...
Недостатки- Затраты на обучение- Дополнительные настроки в проекте- Некоторые тесты сложно писать
TDD вид сбоку
$ pip install unittest2
# test_add.pyimport unittest2class AddTest(unittest2.TestCase):  def test_add(self):    self.assertEquals(add(1, 1), 2)   ...
# test_add.pyimport unittest2def add(a, b):   passclass AddTest(unittest2.TestCase):  def test_add(self):    self.assertEq...
Запуск теста$ python test_add.py
$ python test_add.pyF=========================================FAIL: test_add (__main__.AddTest)---------------------------...
# test_add.pyimport unittest2def add(a, b):  return a + bclass AddTest(unittest2.TestCase):  def test_add(self):    self.a...
$ python test_add.py.-------------------------------------------------Ran 1 test in 0.000sOK
Проект растет - тестов  становится много ... ./tests/ ./tests/test_add.py ./tests/test_sub.py ./tests/test_div.py ./tests/...
Nose - запускалка тестов            Устанавливаем nose$ pip install nose               Запускаем тесты$ nosetests..-------...
Инструментыunittest2       django.testflexmock        django_nosenose            django_webtest
Тестирование в DjangoУстановить приложения   $ pip install django_nose   $ pip install django_webtestСоздать тестовую конф...
# testing_settings.pyfrom settings import *DATABASES = {  "default": dict(    ENGINE = "django.db.backends.sqlite3",    NA...
Запуск тестов в DjangoЗапуск всех тестов в папке ./blog$ manage.py test ./blog --settings project.testing_settingsЗапуск т...
Запуск тестов только для одного класса$ manage.py test ./blog/test/test_forms.py:PostFormTest--settings project.testing_se...
Blog tutorial
Тест viewfrom django.test import TestCase, Clientclass HomePageTest(TestCase):  def test_homepage_is_available(self):    c...
class HomePageTest(TestCase):  def setUp(self):    self.posts = [ ]    for i in range(20):           post = Post.objects.c...
class HomePageTest(TestCase):  def setUp(self):    self.posts = [ ]    for i in range(20):           post = Post.objects.c...
class HomePageTest(TestCase):  def setUp(self):    pass  def tearDown(self):    pass  def test_homepage_contains_posts(sel...
def home(request):  posts = Post.objects.all()[:10]  return render(request, home.html, {posts:posts})
from django.db import modelsclass Post(models.Model):   picture = models.ImageField(       upload_to=posts, blank=True, nu...
Отправка формыclass PostFormTest(TestCase):  def test_post_from_submit(self):    c = Client()    params = {title:Hello Pyc...
Загрузка файловdef test_post_from_submit_with_picture(self):  f = open(blog/tests/fixtures/debian-logo.png)  params = {   ...
$ pip install django_webtest
django_webtest - XPathclass HomePageWebTest(WebTest):  def setUp(self):    ...  def test_homepage_contains_posts(self):   ...
django_webtest - формыfrom django_webtest import WebTestclass PostFormWebTest(WebTest):  def test_post_from_submit(self): ...
Тесты админкиПочти такие же как тесты других view
class PostAdminTest(TestCase):  def setUp(self):    self.user = User.objects.create_user(       admin,       mail@example....
class PostAdminTest(TestCase):  def setUp(self):    ...  def test_post_form_submit(self):    c = Client()    c.login(usern...
Прочее в Django- Middleware- Template tags, filters- Context processors- тестируются модульными тестами какпростые функции...
Особенности тестов view в Django     ----------------------------     middleware     -----------------------------     con...
Flexmock- Заменять части объектов и классов- Заменять функции, в том числевстроенные- Создавать объекты заглушки- Проверят...
$ pip install flexmock
from flexmock import flexmockfrom blog.models import Postdef test_home_page_with_flexmock(self):  posts = [     Post(title...
from flexmock import flexmockimport blog.viewsdef test_home_view_as_unittest(self):  request = flexmock(     GET={},     P...
Теория 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.assertEqual...
Тестирование реализацииПишем тест имея представление о внутренностяхdef get_url_content(url):  try:     response = urllib....
Тест для случая нормального        выполнения def test_get_url_content(self):   url = http://example.com   response = Stri...
Тест в случае ошибки сетиdef test_get_url_content_on_ioerror(self):  url = http://example.com  (flexmock(urllib)     .shou...
Примеры тестовhttps://bitbucket.org/ishalyapin/python-test-exampleshttps://bitbucket.org/ishalyapin/django-test-examples
Спасибо за внимание!  Доклад подготовили Илья Шаляпин ishalyapin@gmail.com www.ishalyapin.ru www.bookradar.org bitbucket.o...
Upcoming SlideShare
Loading in...5
×

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

3,034

Published on

Презентация доклада Ильи Шаляпина и Евгения Генералова в первого Pycon'а в России.

0 Comments
2 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
3,034
On Slideshare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
26
Comments
0
Likes
2
Embeds 0
No embeds

No notes for slide

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

  1. 1. Разработка через тестированиев Python и Django Илья Шаляпин Евгений Генералов
  2. 2. 19 проектов 4 года89299 строк кода50826 строк тестов
  3. 3. Писать тесты или нет?
  4. 4. Пример из жизниПереезд с Ubuntu 8.04 на Ubuntu 12.04Python 2.5 Python 2.7Django 1.3 Django 1.4.0lxml 1.3.6 lxml 2.3.2PIL 1.1.6 PIL 1.1.7... ...
  5. 5. Перезд проекта плотно покрытого тестами
  6. 6. Перезд проекта менее плотно покрытого тестами
  7. 7. Перезд проекта без тестов
  8. 8. Преимущества- Меньше ручной работы- Спокойный рефакторинг- Код легче читать- Быстрое подключение людей к проекту- Тесты являются спецификацией
  9. 9. Недостатки- Затраты на обучение- Дополнительные настроки в проекте- Некоторые тесты сложно писать
  10. 10. TDD вид сбоку
  11. 11. $ pip install unittest2
  12. 12. # test_add.pyimport unittest2class 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. 13. # test_add.pyimport unittest2def add(a, b): passclass AddTest(unittest2.TestCase): def test_add(self): self.assertEquals(add(1, 1), 2)if __name__ == __main__: unittest2.main()
  14. 14. Запуск теста$ python test_add.py
  15. 15. $ python test_add.pyF=========================================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.000sFAILED (failures=1)
  16. 16. # test_add.pyimport unittest2def add(a, b): return a + bclass AddTest(unittest2.TestCase): def test_add(self): self.assertEquals(add(1, 1), 2)if __name__ == __main__: unittest2.main()
  17. 17. $ python test_add.py.-------------------------------------------------Ran 1 test in 0.000sOK
  18. 18. Проект растет - тестов становится много ... ./tests/ ./tests/test_add.py ./tests/test_sub.py ./tests/test_div.py ./tests/test_mul.py ./tests/test_pi.py
  19. 19. Nose - запускалка тестов Устанавливаем nose$ pip install nose Запускаем тесты$ nosetests..--------------------------------------------Ran 100500 tests in 0.219sOK
  20. 20. Инструментыunittest2 django.testflexmock django_nosenose django_webtest
  21. 21. Тестирование в DjangoУстановить приложения $ pip install django_nose $ pip install django_webtestСоздать тестовую конфигурацию testing_settings.py
  22. 22. # testing_settings.pyfrom settings import *DATABASES = { "default": dict( ENGINE = "django.db.backends.sqlite3", NAME = ":memory:", )}INSTALLED_APPS += ( django_nose,)TEST_RUNNER = django_nose.NoseTestSuiteRunner
  23. 23. Запуск тестов в DjangoЗапуск всех тестов в папке ./blog$ manage.py test ./blog --settings project.testing_settingsЗапуск тестов в одном файле$ manage.py test ./blog/test/test_forms.py --settingsproject.testing_settings
  24. 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
  25. 25. Blog tutorial
  26. 26. Тест viewfrom django.test import TestCase, Clientclass HomePageTest(TestCase): def test_homepage_is_available(self): c = Client() response = c.get(/) self.assertEquals(response.status_code, 200)
  27. 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. 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. 29. class HomePageTest(TestCase): def setUp(self): pass def tearDown(self): pass def test_homepage_contains_posts(self): pass
  30. 30. def home(request): posts = Post.objects.all()[:10] return render(request, home.html, {posts:posts})
  31. 31. from django.db import modelsclass 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. 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. 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. 34. $ pip install django_webtest
  35. 35. django_webtest - XPathclass 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. 36. django_webtest - формыfrom django_webtest import WebTestclass 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. 37. Тесты админкиПочти такие же как тесты других view
  38. 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. 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. 40. Прочее в Django- Middleware- Template tags, filters- Context processors- тестируются модульными тестами какпростые функции, аналогично спримером 1+1 = 2
  41. 41. Особенности тестов view в Django ---------------------------- middleware ----------------------------- context processors ----------------------------- template ----------------------------- view ----------------------------- models ----------------------------- network
  42. 42. Flexmock- Заменять части объектов и классов- Заменять функции, в том числевстроенные- Создавать объекты заглушки- Проверять ожидания (сколько развызван метод, с какими аргументами)
  43. 43. $ pip install flexmock
  44. 44. from flexmock import flexmockfrom blog.models import Postdef 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. 45. from flexmock import flexmockimport blog.viewsdef 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)
  46. 46. Теория vs практика
  47. 47. Есть требования ...def get_url_content(url): # ToDo # Вернуть контент страницы # или None, в случае ошибки pass
  48. 48. Как написать тест?def test_get_url_content(self): url = http://example.com text = get_url_content(url) self.assertEquals(text, ???)
  49. 49. Тестирование реализацииПишем тест имея представление о внутренностяхdef get_url_content(url): try: response = urllib.urlopen(url) content = response.read() response.close() except IOError: return None return contentНеверно с точки зрения теории,удобно на практике
  50. 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. 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)
  52. 52. Примеры тестовhttps://bitbucket.org/ishalyapin/python-test-exampleshttps://bitbucket.org/ishalyapin/django-test-examples
  53. 53. Спасибо за внимание! Доклад подготовили Илья Шаляпин ishalyapin@gmail.com www.ishalyapin.ru www.bookradar.org bitbucket.org/ishalyapin github.com/un1t Евгений Генералов e.generalov@gmail.com github.com/generalov
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×