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

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

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

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

Moscow Python Meetup

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 процесса