Successfully reported this slideshow.
Upcoming SlideShare
×

# Тестирование и Django

1,942 views

Published on

Когда тестировать, что тестировать, как тестировать, Как ускорить тесты и упростить их написание. Отказываемся от классических фикстур в пользу динамически создаваемых моделей.

Published in: Technology
• Full Name
Comment goes here.

Are you sure you want to Yes No
• I've found your course really helpful and it's saved a lot of time so I can focus on my other subjects too such as English and Science. Your course has taught me a number of techniques to solve questions quicker. For instance, how to quickly turn recurring decimals into fractions. This has saved a lot of time when completing my mock exams. Thanks Jeevan. Your course is great! ♣♣♣ http://t.cn/AirrSv7D

Are you sure you want to  Yes  No
• After many failed attempts, I almost packed in my GCSE maths altogether. But fortunately I didn't, thanks to Jeevan's guide! When I read it, I found out exactly where I was going wrong all this time! I followed his approach and achieved 90% in my next sitting. I was shocked and I thought it was a total fluke so I put his strategy to the test again. This time, I got 100%! Fantastic! If only I came across Jeevan's strategy sooner. Learn more.. ★★★ http://t.cn/AirrSv7D

Are you sure you want to  Yes  No

### Тестирование и Django

1. 1. Тестирование и Django Илья Барышев @coagulant Moscow Django Meetup №6
2. 2. Защита отрегрессий
3. 3. Быстрыеизменения в коде
4. 4. Меняет подход кнаписанию кода
5. 5. Пойдёт на пользу вашему проекту
6. 6. Модульноетестирование
7. 7.        def  test_vin_is_valid(self):                valid_vins  =  (2G1FK1EJ7B9141175,                                            11111111111111111,)                for  valid_vin  in  valid_vins:                        self.assertEqual(vin_validator(valid_vin),  None)        def  test_vin_is_invalid(self):                invalid_vins  =  (abc,  uM05C0WDJAN60M33TUP6,)                for  invalid_vin  in  invalid_vins:                        self.assertRaises(ValidationError,                              vin_validator,  invalid_vin)
8. 8. UnittestМодели Контекст-процессорыФормы MiddlewareViews? Template tags, ﬁlters
9. 9. Тестируйте поведение А не имплементацию
10. 10. Функциональное тестирование
11. 11. django.test.client.Clientdef  testPostAsAuthenticatedUser(self):        data  =  self.getValidData(Article.objects.get(pk=1))        self.client.login(username="normaluser",                                              password="normaluser")        self.response  =  self.client.post("/post/",  data)                self.assertEqual(self.response.status_code,  302)        self.assertEqual(Comment.objects.count(),  1)
12. 12. django.test.сlient.RequestFactorydef  test_post_ok(self):        request  =  RequestFactory().post(reverse(ch_location),                                                                        {location_id:  77})        request.cookies  =  {}        response  =  change_location(request)        self.assertEqual(response.cookies[LOCATION].value,  77)        self.assertEqual(response.status_code,  302)
13. 13. Smoke Testing
14. 14. def  test_password_recovery_smoke(self):        """        Урлы  восстановления  пароля.        Логика  уже  протестирована  в  django-­‐password-­‐reset        """        response_recover  =  self.client.get(reverse(pass_recover))                self.assertEqual(response_recover.status_code,  200)                self.assertContains(response_recover,                self.assertTemplateUsed(response_recover,                                                        uВосстановление  пароля)                                                                password_reset/recovery_form.html)
15. 15. Как мы тестируем
16. 16. ContiniousIntegration
17. 17. Покрытие важно Но не делайте из него фетиш
18. 18. mockhttp://www.voidspace.org.uk/python/mock/
19. 19. >>>  real  =  SomeClass()>>>  my_mock  =  MagicMock(name=method)>>>  real.method  =  my_mock>>>  real.method(3,  4,  5,  key=value)>>>  my_mock.calledTrue>>>  my_mock.call_count1>>>  mock.method.assert_called_with(3,  4,  5)Traceback  (most  recent  call  last):    ...AssertionError:  Expected  call:  method(3,  4,  5)Actual  call:  method(3,  4,  5,  key=value)
20. 20. @patch(twitter.Api)def  test_twitter_tag_simple_mock(self,  ApiMock):        api_instance  =  ApiMock.return_value        api_instance.GetUserTimeline.return_value  =  SOME_JSON        output,  context  =  render_template( """{%  load  twitter_tag  %}  {%  get_tweets  for  "jresig"  as  tweets  %}""")        api_instance.GetUserTimeline.assert_called_with(                screen_name=jresig,                  include_rts=True,                  include_entities=True)
21. 21. from  mock  import  patchfrom  django.conf  import  settings@patch.multiple(settings,  APPEND_SLASH=True,                                MIDDLEWARE_CLASSES=(common_middleware,))def  test_flatpage_doesnt_require_trailing_slash(self):        form  =  FlatpageForm(data=dict(url=/no_trailing_slash,                                                                      **self.form_data))        self.assertTrue(form.is_valid())
22. 22. from  django.test.utils  import  override_settings@override_settings(        APPEND_SLASH=False,          MIDDLEWARE_CLASSES=(common_middleware,))def  test_flatpage_doesnt_require_trailing_slash(self):        form  =  FlatpageForm(data=dict(url=/no_trailing_slash,                                                                      **self.form_data))        self.assertTrue(form.is_valid())
23. 23. Фикстуры
24. 24. [ { "model": "docs.documentrelease", "pk": 1, "fields": { "lang": "en", "version": "dev", "scm": "svn", "scm_url": "http://code.djangoproject.com/svn/django/trunk/docs", "is_default": false } }, { "model": "docs.documentrelease", Обычный тест с "pk": 2, фикстурами "fields": { "lang": "en", "version": "1.0", "scm": "svn", "scm_url": "http://code.djangoproject.com/svn/django/branches/releases/1.0.X/docs", "is_default": false } }, { "model": "docs.documentrelease", "pk": 3, "fields": { "lang": "en", "version": "1.1", "scm": "svn", "scm_url": "http://code.djangoproject.com/svn/django/branches/releases/1.1.X/docs", "is_default": false
25. 25. django-­‐any https://github.com/kmmbvnr/django-­‐anyfrom  django_any  import  any_modelclass  TestMyShop(TestCase):        def  test_order_updates_user_account(self):                account  =  any_model(Account,  amount=25,                              user__is_active=True)                order  =  any_model(Order,  user=account.user,                        amount=10)                order.proceed()                account  =  Account.objects.get(pk=account.pk)                self.assertEquals(15,  account.amount)
26. 26. factory_boyhttps://github.com/dnerdy/factory_boy
27. 27. import  factoryfrom  models  import  MyUserclass  UserFactory(factory.Factory):        FACTORY_FOR  =  MyUser        first_name  =  John        last_name  =  Doe        admin  =  False
28. 28. #  Инстанс,  сохранённый  в  базуuser  =  UserFactory.create()#  Экземпляр  User,  не  сохранённый  в  базуuser  =  UserFactory.build()#  Создаём  инстанс  с  конкретыми  значениямиuser  =  UserFactory.create(name=uВасилий,  age=25)
29. 29. class  UserFactory(factory.Factory):        first_name  =  Vasily        last_name  =  Pupkin        email  =  factory.LazyAttribute( lambda  u:  {0}.{1}@example.com.format( u.first_name,  u.last_name).lower())>>>  UserFactory().emailvasily.pupkin@example.com
30. 30. class  UserWithEmailFactory(UserFactory):        email  =  factory.Sequence( lambda  n:  person{0}@example.com.format(n))>>>  UserFactory().emailperson0@example.com>>>  UserFactory().email    person1@example.com
31. 31. Django test runner SUCKS
32. 32. INSTALLED_APPS  =  (        ...        #3rd-­‐party  apps        south,        sorl.thumbnail,        pytils,        pymorphy,                  compressor, Несколько сотен        django_nose, тестов        django_geoip,        mptt,        widget_tweaks,        guardian,                ...
33. 33. /tests #  -­‐*-­‐  coding:  utf-­‐8  -­‐*-­‐        __init__.py test_archive.py from  test_archive  import  *        test_blog_model.py from  test_blog_model  import  *        test_modified.py from  test_modified  import  *        test_post_model.py from  test_post_model  import  *        test_redactor.py from  test_redactor  import  *        test_views.py from  test_views  import  *        test_cross_post.py from  test_cross_post  import  *
34. 34. django-­‐nose https://github.com/jbalogh/django-­‐nose
35. 35. \$  pip  install  django-­‐nose #  settings.py   INSTALLED_APPS  =  (        ...        django_nose,        ... ) TEST_RUNNER  =  django_nose.NoseTestSuiteRunner
36. 36. \$  manage.py  test\$  manage.py  test  apps.comments.tests\$  manage.py  test  apps.comments.tests:BlogTestCase\$  manage.py  test  apps.comments.tests:BlogTestCase.test_index\$  manage.py  test  -­‐-­‐with-­‐ids  -­‐-­‐failed\$  manage.py  -­‐-­‐pdb\$  manage.py  -­‐-­‐pdb-­‐failures
37. 37. from  nose.plugins.attrib  import  attr@attr(speed=slow,  priority=1)def  test_big_download():        import  urllib        #  commence  slowness..\$  nosetests  -­‐a  speed=slow\$  nosetests  -­‐a  !slow\$  nosetests  -­‐A  "(priority  >  5)  and  not  slow"
38. 38. TESTINGTESTING
39. 39. SQLite длябыстрых тестов Если ваш проект позволяет
40. 40. Параллелим тесты Нетрудоёмкое ускоение
41. 41. Ran  337  tests  in  326.664s OK  (SKIP=2)1 процесс 3262 процесса Секунды 1693 процесса 126 0 100 200 300 400 \$  ./manage.py  -­‐-­‐processes=N
42. 42. Спасибо за вниманиеbaryshev@futurecolors.ru@coagulant http://blog.futurecolors.ru/