Testing applications Django with py.test (Django Stockholm meetup)

Testing applications Django with py.test (Django Stockholm meetup)

Slides for a talk that was given at the Django Stockholm meetup 2013-04-29.

0aa514c079b74c6655e6a8bf1073c878?s=128

Andreas Pelme

April 29, 2013
Tweet

Transcript

  1. 5FTUJOH%KBOHP BQQMJDBUJPOTXJUI QZUFTU "OESFBT1FMNFBOESFBT!QFMNFTF 5XJUUFS!BOESFBTQFMNF

  2. .F w $PGPVOEFSEFWFMPQFSBU1FSTPOBMLPMMFO w 1ZUIPOQSPGFTTJPOBMMZTJODF XSJUJOHUFTUT TJODF

  3. 5FTUJOHJO%KBOHP

  4. 5FTU$BTFIJFSBSDIZ

  5. None
  6. .VMUJQMFUFTUpMFT # Typical contents of app/tests/__init__.py: from .test_foo import *

    from .test_bar import * from .test_spam import *
  7. QZUFTU w (FOFSBMQVSQPTF1ZUIPOUFTUJOHUPPM w 6TFECZ1Z1Z .P[JMMB TJY 4FOUSZ  w

    *OOPWBUJWFGFBUVSFT pYUVSFT QPXFSGVMQMVHJO TZTUFN w -PUTPGFYJTUJOHQMVHJOT w $PWFSBHF EJTUSJCVUFEUFTUJOH 1&1DIFDLJOH w %KBOHPQMVHJOQZUFTUEKBOHP w 8SJUFUFTUTJOBQZUIPOJDTUZMF
  8. QZUFTUUFTUTUZMF def  greeter(name):        return  'Hello  %s!'  %

     name def  test_greeter():        assert  greeter('Andreas')  ==  'Hello  Andreas'
  9. None
  10. None
  11. QZUFTUEKBOHP w *OUFHSBUFTQZUFTUBOE%KBOHP w .BOBHFTUIFUFTUEBUBCBTF w "MMPXTEBUBCBTFSFVTF w 1SPWJEFT%KBOHPUFTUIFMQFSTJOBQZUFTUXBZ w

    $IBOHJOHPGTFUUJOHT w -JWF4FSWFS w VSMDPOGPWFSSJEFT w *OWPLFEWJBpy.testOPUmanage.py  test
  12. QZUFTUEKBOHPTFUVQ w pip  install  pytest-­‐django   w 3FRVJSFTZPVSDPEFUPCFPO1ZUIPOQBUI w add2virtualenv

     your/project/directory w $POpHVSFTFUUJOHT w 4FUDJANGO_SETTINGS_MODULEJOZPVSTIFMM w 03DSFBUFQZUFTUJOJBOETFU DJANGO_SETTINGS_MODULE=mysettings w #POVT3VO1ZUIPOTIFMMEJSFDUMZXJUIpython
  13. 8IFSFUPQVUUFTUT w #ZEFGBVMUBOZpMFUIBUNBUDIFTtest_*.py w 5FTUEJTDPWFSZJTDPOpHVSBCMF w 4FUpython_files  =  *.pyJOQZUFTUJOJ w

    5FTUTJOBQQEJSFDUPSZ w BQQUFTUTWJFXTQZ w 5FTUTJOTFQBSBUFEJSFDUPSZ w VOJU@UFTUTBQQWJFXTQZ w JOUFHSBUJPO@UFTUTUFTU@TPNFUIJOHQZ w %FQFOETPOZPVSQSPKFDU
  14. &YJTUJOHUFTUTVJUF w %KBOHP5FTU$BTFBOEVOJUUFTU5FTU$BTF TVCDMBTTFTJTQJDLFEVQBOESVOBVUPNBUJDBMMZ

  15. )PXUPSVOUFTUT #  run  all  tests $  py.test #  run  a

     specific  file $  py.test  tests/app/test_views.py #  run  test  which  matches  tests  named  *test_foo* $  py.test  tests/app/test_views.py  -­‐k  test_foo #  run  all  non  database  tests  (more  on  this  later) $  py.test  tests/app  -­‐m  'not  django_db'
  16. l5IFSFJTOPTFDSFUUPXSJUJOH UFTUTUIFSFBSFPOMZTFDSFUT UPXSJUJOHUFTUBCMFDPEFz .JãLP)FWFSZ

  17. 5FTUBCMF%KBOHPDPEF w %BUBCBTFBDDFTTNBLFTUFTUTTMPX w %KBOHP`T03.BOENPEFMTJTHSFBU w 5IFEJTUJODUJPOCFUXFFORVFSZDPEFBOE BQQMJDBUJPODPEFDBOCFCMVSSZ w 1VURVFSZDPEFJONBOBHFSNFUIPET

    w 5FTUUIPTFXJUIUIFSFBMEBUBCBTF w 1VUBQQMJDBUJPOMPHJDJONFUIPETXIJDIEPFTOPU UIFRVFSZUIFEBUBCBTF w 4UVCCJOHNPDLJOHDBOIFMQXJUIJTPMBUJPO
  18. 5FTUBCMF%KBOHPDPEF w ,FFQWJFXTWFSZTMJN w 1VTIMPHJDUPGPSNT NPEFMNFUIPETBOE VUJMJUZGVODUJPOTUIBUDBOCFUFTUFEJO JTPMBUJPO w "WPJECSBODIFTJOWJFXTXIFOQPTTJCMF

    w 5FTUWJFXTXJUIBDPVQMFPGIJHIMFWFMUFTUT XJUIGVMMEBUBCBTFBDDFTT w 4FMFOJVNBOEXFCUFTUBSFHPPEUPPMT
  19. 5FTUBCMF%KBOHPDPEF w %PO`UCFTDBSFEPGDSFBUJOHPUIFSNPEVMFT FYDFQUWJFXTQZ NPEFMTQZ w 5IFNPSFPGZPVSBQQMJDBUJPOUIBUEPFTOPU EFQFOEPO%KBOHPUIFCFUUFS w .BLFVTFPGUIF1ZUIPONPEVMFTZTUFN

    JUJT SFBMMZHPPE 
  20. None
  21. %BUBCBTFVTBHF w 8JUIQZUFTUEKBOHP ZPVNVTUCFFYQMJDJUBCPVU EBUBCBTFBDDFTT w %BUBCBTFBDDFTTJTSFRVFTUFEXJUIUIF pytest.mark.django_dbNBSLFS

  22. %BUBCBTFVTBHF import  pytest def  test_without_db_access():        #  any

     db  accesses  will  raise  an  error        pass @pytest.mark.django_db def  test_with_db_access():        #  test  db  is  available        pass
  23. %BUBCBTFVTBHF import  pytest #  Allow  all  tests  in  this  module

     database  access pytestmark  =  pytest.mark.django_db def  test_with_db_access_1():        pass def  test_with_db_access_2():        pass def  test_with_db_access_3():        pass
  24. 5FTUEBUBCBTFSFVTF w 5FTUTTIPVMESVOBHBJOTUUIFSFBMEBUBCBTF w 5BCMFDSFBUJPOUBLFTUJNF w QZUFTUEKBOHPQSPWJEFTUIF-­‐-­‐reuse-­‐dbPQUJPO w 1VUaddopts  =

     -­‐-­‐reuse-­‐dbJOQZUFTUJOJ w 1FSTJTUUIFUFTUEBUBCBTFCFUXFFOSVOT w 8IFOVTJOH-­‐-­‐reuse-­‐db QBTT-­‐-­‐create-­‐dbUP GPSDFSFDSFBUJPOPGUIFEBUBCBTF
  25. .PEFMUFTUEBUB w )PXUPHFUNPEFMJOTUBODFTGPSUFTUT w $SFBUFPCKFDUTNBOVBMMZ w %KBOHPpYUVSFT OPUQZUFTUpYUVSFT w 'BDUPSJFT

  26. %KBOHPUFTUpYUVSFT [ { "model": "myapp.person", "pk": 1, "fields": { "first_name":

    "John", "last_name": "Lennon" } }, { "model": "myapp.person", "pk": 2, "fields": { "first_name": "Paul", "last_name": "McCartney" } } ]
  27. 'BDUPSJFTGBDUPSZ@CPZ w )FMQFSGPSDSFBUJOHUFTUPCKFDUT w 4VQQPSUTBVUPNBUJDBMMZDSFBUJOHSFMBUFENPEFMT w .BLFTNPEFMUFTUEBUBNBJOUBJOBCMF w 0WFSSJEFUIFTQFDJpDBUUSJCVUFTUIBUBSF JOUFSFTUJOHJOBUFTU

    w $BOCFVTFEJOJOUFHSBUJPOUFTUTBOEVOJUUFTUT
  28. GBDUPSZ@CPZFYBNQMF #  models.py from  django.db  import  models class  Group(models.Model):  

         name  =  models.TextField() class  Person(models.Model):        first_name  =  models.TextField()        last_name  =  models.TextField()        group  =  models.ForeignKey(Group)        def  group_letter(self):                return  self.group.name[0].upper()        def  full_name(self):                return  '%s  %s'  %  (self.first_name,                                                    self.last_name) #  factories.py import  factory from  app.models  import  Person,  Group class  GroupFactory(factory.Factory):        FACTORY_FOR  =  Group        name  =  'Developers' class  PersonFactory(factory.Factory):        FACTORY_FOR  =  Person        first_name  =  'John'        last_name  =  'Doe'        group  =  factory.SubFactory(GroupFactory)
  29. GBDUPSZ@CPZFYBNQMF from  factories  import  PersonFactory def  test_full_name():      

     person  =  PersonFactory.build(first_name='Andreas',                                                                  last_name='Pelme')        assert  person.full_name()  ==  'Andreas  Pelme' def  test_group_letter():        person  =  PersonFactory.build(group__name='admins')        assert  person.group_letter()  ==  'A'
  30. QZUFTUpYUVSFT w /PUUPCFDPOGVTFEXJUI%KBOHPpYUVSFT w "XBZPGDPOTUSVDUJOHUFTUPCKFDUTBOEUFTU EFQFOEFODJFT w 4JNJMBSUPTFU6QUFBS%PXOCVUNPSFFYQMJDJU BOENPEVMBS

  31. QZUFTUpYUVSFT import  pytest from  my_module  import  MyClass @pytest.fixture def  my_object():

           return  MyClass() def  test_foo(my_object):        assert  my_object.foo()  ==  'foo' def  test_bar(my_object):        assert  my_object.bar()  ==  'bar'
  32. QZUFTUEKBOHPpYUVSFT def  test_changing_settings(settings):        settings.DATE_FORMAT  =  'Y-­‐m-­‐d'  

         #  call  code  that  uses  settings.DATE_FORMAT  ...          #  the  settings  will  be  automatically  reset  after  this  test def  test_with_testclient(client):        #  client  is  an  instance  of  Django's  TestClient,  just  like        #  self.client  in  a  Django  TestCase        response  =  client.get('/foo/bar')        assert  response.status_code  ==  200
  33. 'BDUPSZ pYUVSFT import  pytest from  factories  import  PersonFactory @pytest.fixture def

     person():        return  PersonFactory.build() def  test_with_person(person):        #  Test  something  on  the  Person  object        pass
  34. 'BDUPSZ pYUVSFT import  pytest from  factories  import  UserFactory @pytest.fixture def

     user_in_db(db):        #  The  db  argument  makes  database  access  allowed        return  UserFactory.create() @pytest.mark.django_db def  test_with_saved_user(user_in_db):        #  user_in_db  is  a  saved  user        pass
  35. (SFBUSFTPVSDFT w )PMHFS,SFLFM QZUFTU 1Z$PO w *OUSPEVDUJPOUPQZUFTU TIPXTQZUFTUGFBUVSFTJOEFQUI w IUUQXXXZPVUVCFDPNXBUDI

    W-7R#2D'NZX w $BSM.FZFS 5FTUJOHBOE%KBOHP 1Z$PO w "MPUPG%KBOHPUFTUJOHCFTUQSBDUJDFT w IUUQXXXZPVUVCFDPNXBUDI WJDL/2D/9J4 w QZUFTUEPDVNFOUBUJPO w IUUQQZUFTUPSH w QZUFTUEKBOHPEPDVNFOUBUJPO w IUUQQZUFTUEKBOHPSFBEUIFEPDTPSH
  36. None