Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

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

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

Avatar for Moscow Python Meetup

Moscow Python Meetup PRO

October 24, 2012
Tweet

More Decks by Moscow Python Meetup

Other Decks in Technology

Transcript

  1.        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',  u'M05C0WDJAN60M33TUP6',)                for  invalid_vin  in  invalid_vins:                        self.assertRaises(ValidationError,                              vin_validator,  invalid_vin)
  2. django.test.client.Client def  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)
  3. django.test.сlient.RequestFactory def  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)
  4. 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,                                                        u'Восстановление  пароля')                self.assertTemplateUsed(response_recover,                                                                'password_reset/recovery_form.html')
  5. >>>  real.method(3,  4,  5,  key='value') >>>  my_mock.called True >>>  my_mock.call_count

    1 >>>  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') >>>  real  =  SomeClass() >>>  my_mock  =  MagicMock(name='method') >>>  real.method  =  my_mock
  6. @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)
  7. from  mock  import  patch from  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())
  8. 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())
  9. Обычный тест с фикстурами [ { "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
  10. django-­‐any https://github.com/kmmbvnr/django-­‐any from  django_any  import  any_model class  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)
  11. import  factory from  models  import  MyUser class  UserFactory(factory.Factory):    

       FACTORY_FOR  =  MyUser        first_name  =  'John'        last_name  =  'Doe'        admin  =  False
  12. #  Экземпляр  User,  не  сохранённый  в  базу user  =  UserFactory.build()

    #  Инстанс,  сохранённый  в  базу user  =  UserFactory.create() #  Создаём  инстанс  с  конкретыми  значениями user  =  UserFactory.create(name=u'Василий',  age=25)
  13. 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().email '[email protected]'
  14. class  UserWithEmailFactory(UserFactory):        email  =  factory.Sequence( lambda  n:

     'person{0}@example.com'.format(n)) >>>  UserFactory().email '[email protected]' >>>  UserFactory().email     '[email protected]'
  15. INSTALLED_APPS  =  (        ...      

     #3rd-­‐party  apps        'south',        'sorl.thumbnail',        'pytils',        'pymorphy',                  'compressor',        'django_nose',        'django_geoip',        'mptt',        'widget_tweaks',        'guardian',                ... Несколько сотен тестов
  16. /tests        __init__.py test_archive.py        test_blog_model.py

           test_modified.py        test_post_model.py        test_redactor.py        test_views.py        test_cross_post.py #  -­‐*-­‐  coding:  utf-­‐8  -­‐*-­‐ from  test_archive  import  * from  test_blog_model  import  * from  test_modified  import  * from  test_post_model  import  * from  test_redactor  import  * from  test_views  import  * from  test_cross_post  import  *
  17. $  pip  install  django-­‐nose #  settings.py   INSTALLED_APPS  =  (

           ...        'django_nose',        ... ) TEST_RUNNER  =  'django_nose.NoseTestSuiteRunner'
  18. $  manage.py  test  -­‐-­‐with-­‐ids  -­‐-­‐failed $  manage.py  -­‐-­‐pdb $  manage.py

     -­‐-­‐pdb-­‐failures $  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
  19. 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"
  20. Секунды 0 100 200 300 400 126 169 326 Ran

     337  tests  in  326.664s OK  (SKIP=2) $  ./manage.py  -­‐-­‐processes=N 1 процесс 2 процесса 3 процесса