pytest: helps you write better Django apps

pytest: helps you write better Django apps

Slides for the talk I gave at DjangoCon Europe 2014

0aa514c079b74c6655e6a8bf1073c878?s=128

Andreas Pelme

May 15, 2014
Tweet

Transcript

  1. QZUFTU IFMQTZPVXSJUFCFUUFS %KBOHPBQQT "OESFBT1FMNF %KBOHP$PO&VSPQF 4MJEFTIUUQTQFBLFSEFDLDPNQFMNF personalkollen

  2. 5PQJDT w5FTUJOHJO%KBOHPPWFSWJFX w*OUSPEVDUJPOUPQZUFTU w%KBOHPBOEQZUFTU w5FTUPSHBOJ[BUJPO w'BTUFSUFTUSVOT wQZUFTUpYUVSFT

  3. 5FTUJOHJO%KBOHP

  4. BGVMMGFBUVSFE1ZUIPOUFTUJOHUPPM QZUFTU

  5. None
  6. 1MVHJOT w%KBOHPQZUFTUEKBOHP w%JTUSJCVUFEQBSBMMFMJ[FEQZUFTUYEJTU w-PHDBQUVSFQZUFTUDBQUVSFMPH w$PWFSBHFSFQPSUJOHQZUFTUDPW wBOENBOZNPSF wIUUQQZUFTUPSHMBUFTUQMVHJOTIUNMFYUFSOBM QMVHJOT

  7. XJUIPVUCPJMFSQMBUF 1ZUIPOJDUFTUT

  8. %FNPUJNF

  9. %FNP#FGPSF from  django.test  import  TestCase   ! class  TestHelloWorld(TestCase):  

           def  test_hello_world(self):                  response  =  self.client.get('/')   !                self.assertEqual(response.status_code,  200)                  self.assertIn('Hello',                                              response.content.decode('utf-­‐8'))
  10. %FNP"GUFS def  test_hello_world(client):          response  =  client.get('/')

      !        assert  response.status_code  ==  200          assert  'Hello'  ==  response.content.decode('utf-­‐8')
  11. %FNPUJNF w"TTFSUJPOTXJUIBTTFSUTUBUFNFOU w/P5FTU$BTFTVCDMBTTJOH wQZUFTUpYUVSFTGPSUFTUEFQFOEFODJFT

  12. *OUFHSBUJPOCFUXFFOQZUFTUBOE%KBOHP QZUFTUEKBOHP

  13. *OTUBMMTQZUFTUEKBOHPBOEQZUFTU pip  install  pytest-­‐django

  14. "EETZPVSQSPKFDUUP1ZUIPOQBUI SFRVJSFTTFUVQQZUPCFQSFTFOU pip  install  -­‐e  .

  15. "WFSZNJOJNBMTFUVQQZ from  setuptools  import  setup   setup(name='yourproject',  version='1.0')

  16. $POpHVSFTFUUJOHTJOQZUFTUJOJ [pytest] DJANGO_SETTINGS_MODULE=settings

  17. %KBOHP5FTU$BTFT KVTUXPSLT

  18. 3VOOJOHUIFUFTUT

  19. 3VOBMMUIFUFTUT $  py.test

  20. 3VOBMMUFTUTJOBTQFDJpDpMF $  py.test  test_views.py

  21. 3VOBMMUFTUDBTFTUIBUBSFOBNFE UFTU@GPP $  py.test  -­‐k  test_foo

  22. 5FTUPSHBOJ[BUJPO

  23. #ZEFGBVMU UFTUTBSFGPVOEJO test_*.py

  24. $POpHVSBCMFJOQZUFTUJOJ [pytest] python_files  =  check_*.py

  25. 5FTUTDBOMJWFVOEFS%KBOHPBQQT polls/tests/views.py blog/tests/views.py

  26. 5FTUTDBOMJWFJOUIFJSPXOSPPUEJSFDUPSZ tests/polls/views.py tests/blog/models.py

  27. 5FTUTCFTQMJUVQJOUPEJGGFSFOUSPPU EJSFDUPSJFT unit_tests/blog/models.py integration_tests/test_foo.py   browser_tests/test_login.py

  28. None
  29. testing Running tests

  30. 5FTUEBUBCBTFSFVTF

  31. 3FVTFEBUBCBTFGSPNMBTUSVO py.test  -­‐-­‐reuse-­‐db

  32. 1VUUIJTJOQZUFTUJOJUPBMXBZTSFVTFUIF EBUBCBTF ! ! [pytest] addopts  =  -­‐-­‐reuse-­‐db

  33. 3FDSFBUFEBUBCBTFTDIFNBXIFOEPJOH NPEFMDIBOHFT py.test  -­‐-­‐reuse-­‐db  -­‐-­‐create-­‐db

  34. .BLFVTFPGUIPTFFYUSBDPSFT 3VOUFTUTJOQBSBMMFM

  35. YEJTUQSPWJEFTEJTUSJCVUFEUFTUJOH pip  install  pytest-­‐xdist

  36. 3VOUFTUTJOGPVSQBSBMMFMQSPDFTTFT $  py.test  -­‐n  4

  37. QZUFTUEKBOHPBOEEBUBCBTFT

  38. 5IJTXJMMBMMPXEBUBCBTFBDDFTT import  pytest ! @pytest.mark.django_db   def  test_user_count():    

       assert  User.objects.count()  ==  0
  39. 5IJTXJMMGBJM EBUBCBTFBDDFTTJTOPU BMMPXFE def  test_user_count():        assert  User.objects.count()

     ==  0
  40. ! ! ! ___________  test_user_count  ____________   test_db_access.py:4:  in  test_user_count

      >      assert  User.objects.count()  ==  0   ! ... ! E      Failed:  Database  access  not  allowed,        use  the  "django_db"  mark  to  enable
  41. .BSLBOFOUJSFNPEVMFGPSEBUBCBTF VTBHF import  pytest   ! pytestmark  =  pytest.mark.django_db  

    ! def  test_foo():          pass   def  test_bar():          pass
  42. 3VOBMMUFTUTXIJDIEPFTOPUUPVDIUIF EBUBCBTF $  py.test  -­‐m  'not  django_db'

  43. 3FQMBDFNFOUGPSVOJUUFTUTFU6QUFBS%PXO QZUFTUpYUVSFT

  44. [{"pk":  1,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐09T09:10:00Z",  "is_scheduled":  true,  "stop":

      "2011-­‐08-­‐09T09:40:00Z",  "logged_time":  31946}},  {"pk":  2,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐10T09:00:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐08-­‐10T09:30:00Z",  "logged_time":  32038}},  {"pk":  3,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐08-­‐10T09:10:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐08-­‐10T09:40:00Z",  "logged_time":  32039}},  {"pk":  4,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐11T09:10:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐08-­‐11T09:40:00Z",  "logged_time":   32104}},  {"pk":  5,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐11T09:00:00Z",  "is_scheduled":  true,  "stop":   "2011-­‐08-­‐11T09:30:00Z",  "logged_time":  32105}},  {"pk":  6,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐12T09:00:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐08-­‐12T09:30:00Z",  "logged_time":  32161}},  {"pk":  7,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐08-­‐12T09:10:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐08-­‐12T09:40:00Z",  "logged_time":  32162}},  {"pk":  8,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐15T15:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐08-­‐15T16:00:00Z",  "logged_time":   32390}},  {"pk":  9,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐22T09:00:00Z",  "is_scheduled":  true,  "stop":   "2011-­‐08-­‐22T09:30:00Z",  "logged_time":  32784}},  {"pk":  10,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐23T09:00:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐08-­‐23T09:30:00Z",  "logged_time":  32838}},  {"pk":  11,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐08-­‐24T09:00:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐08-­‐24T09:30:00Z",  "logged_time":  32903}},  {"pk":  12,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐25T09:00:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐08-­‐25T09:30:00Z",  "logged_time":   32964}},  {"pk":  13,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐26T09:00:00Z",  "is_scheduled":  true,  "stop":   "2011-­‐08-­‐26T09:30:00Z",  "logged_time":  33029}},  {"pk":  14,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐29T12:00:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐08-­‐29T12:30:00Z",  "logged_time":  33244}},  {"pk":  15,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐08-­‐29T15:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐08-­‐29T16:00:00Z",  "logged_time":  33253}},  {"pk":  16,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐30T11:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐08-­‐30T12:00:00Z",  "logged_time":   33281}},  {"pk":  17,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐30T12:00:00Z",  "is_scheduled":  true,  "stop":   "2011-­‐08-­‐30T12:30:00Z",  "logged_time":  33283}},  {"pk":  18,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐08-­‐31T12:00:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐08-­‐31T12:30:00Z",  "logged_time":  33341}},  {"pk":  19,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐09-­‐01T11:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐01T12:00:00Z",  "logged_time":  33400}},  {"pk":  20,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐01T15:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐01T16:00:00Z",  "logged_time":   33414}},  {"pk":  21,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐02T11:30:00Z",  "is_scheduled":  true,  "stop":   "2011-­‐09-­‐02T12:00:00Z",  "logged_time":  33453}},  {"pk":  22,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐02T15:30:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐09-­‐02T16:00:00Z",  "logged_time":  33475}},  {"pk":  23,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐09-­‐02T12:00:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐02T12:30:00Z",  "logged_time":  33484}},  {"pk":  24,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐03T09:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐03T10:00:00Z",  "logged_time":   33579}},  {"pk":  25,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐03T10:00:00Z",  "is_scheduled":  true,  "stop":   "2011-­‐09-­‐03T10:30:00Z",  "logged_time":  33586}},  {"pk":  26,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐03T10:30:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐09-­‐03T11:00:00Z",  "logged_time":  33587}},  {"pk":  27,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐09-­‐03T12:00:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐03T12:30:00Z",  "logged_time":  33589}},  {"pk":  28,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐03T12:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐03T13:00:00Z",  "logged_time":   33591}},  {"pk":  29,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐04T09:30:00Z",  "is_scheduled":  true,  "stop":   "2011-­‐09-­‐04T10:00:00Z",  "logged_time":  33700}},  {"pk":  30,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐04T12:00:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐09-­‐04T12:30:00Z",  "logged_time":  33702}},  {"pk":  31,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐09-­‐04T12:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐04T13:00:00Z",  "logged_time":  33703}},  {"pk":  32,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐05T12:00:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐05T12:30:00Z",  "logged_time":   33732}},  {"pk":  33,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐05T12:00:00Z",  "is_scheduled":  true,  "stop":   "2011-­‐09-­‐05T12:30:00Z",  "logged_time":  33734}},  {"pk":  34,  "model":  "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐05T15:30:00Z",   "is_scheduled":  true,  "stop":  "2011-­‐09-­‐05T16:00:00Z",  "logged_time":  33744}},  {"pk":  35,  "model":  "tracker.loggedtimebreak",  "fields":   {"start":  "2011-­‐09-­‐06T11:30:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐06T12:00:00Z",  "logged_time":  33794}},  {"pk":  36,  "model":   "tracker.loggedtimebreak",  "fields":  {"start":  "2011-­‐09-­‐06T12:00:00Z",  "is_scheduled":  true,  "stop":  "2011-­‐09-­‐06T12:30:00Z",  "logged_time":   %KBOHPpYUVSFQZUFTUpYUVSF
  45. (FUBOJOTUBODFPGUIFUFTUDMJFOU ! def  test_with_client(client):          response  =

     client.get('/foo/')        assert  response.status_code  ==  200  
  46. %FNPUJNF

  47. %FpOJOHBpYUVSF QVUUIJTJODPOGUFTUQZ import  pytest   from  selenium.webdriver  import  Firefox  

    ! @pytest.yield_fixture(scope='session')   def  webdriver():          driver  =  Firefox()          yield  driver          driver.quit()
  48. 8JMMIBWF'JSFGPYTUBSUFEXIFOUIFUFTU SVOT def  test_hello(live_server,  webdriver):          webdriver.get(live_server.url

     +  '/')          h1  =  (webdriver                      .find_element_by_css_selector('h1'))   !        assert  h1.text  ==  'Hello  Djangocon'
  49. 3FRVFTU%KBOHPEBUBCBTFBDDFTT JOBpYUVSF import  pytest   ! ! @pytest.fixture   def

     person_in_db(db):          return  Person.objects.create(                  name='Andreas')  
  50. 3FRVFTU%KBOHPEBUBCBTFBDDFTT JOBpYUVSF import  pytest   from  factories  import  PersonFactory  

    ! @pytest.fixture   def  person_in_db(db):          return  PersonFactory.create(                  name='Andreas')   Use factory_boy for creating test model data
  51. .PSFpYUVSFTGFBUVSFT w1BSBNFUSJ[BUJPO w4DPQJOH SFVTFFYQFOTJWFTFUVQ  w"VUPVTFpYUVSFT

  52. 8PSLTXJUICPUI XPSMET

  53. (SFBUSFTPVSDFT w$BSM.FZFS 5FTUJOHBOE%KBOHP 1Z$PO w%KBOHPUFTUJOHCFTUQSBDUJDFT wIUUQXXXZPVUVCFDPNXBUDI WJDL/2D/9J4 ! w)PMHFS,SFLFM QZUFTUSBQJEBOETJNQMFUFTUJOHXJUI1ZUIPO

     &VSP1ZUIPO wIUUQXXXZPVUVCFDPNXBUDI WL;+)6N;9. ! wQZUFTUEPDVNFOUBUJPO wIUUQQZUFTUPSH ! wQZUFTUEKBOHPEPDVNFOUBUJPO wIUUQQZUFTUEKBOHPSFBEUIFEPDTPSH
  54. &NBJMBOESFBT!QFMNFTF 5XJUUFS!BOESFBTQFMNF *3$QZMJCPO'SFFOPEF
 4MJEFTIUUQTQFBLFSEFDLDPNQFMNF 2VFTUJPOT