Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Speaker Deck
PRO
Sign in
Sign up
for free
pytest: helps you write better Django apps
Andreas Pelme
May 15, 2014
Programming
5
730
pytest: helps you write better Django apps
Slides for the talk I gave at DjangoCon Europe 2014
Andreas Pelme
May 15, 2014
Tweet
Share
More Decks by Andreas Pelme
See All by Andreas Pelme
pelme
7
840
pelme
2
1.8k
pelme
7
410
Other Decks in Programming
See All in Programming
viteinfinite
0
210
jun0
3
680
danilop
0
180
kyonmm
2
2.2k
wasabeef
1
580
hr01
1
1.3k
hanakla
2
3.1k
kenmaz
1
100
ken3ypa
0
160
azdaroth
0
100
466548
0
150
zsmb
2
110
Featured
See All Featured
philnash
8
510
sferik
610
54k
destraynor
223
47k
morganepeng
17
1.1k
cromwellryan
101
5.9k
tmm1
61
8.5k
holman
288
130k
pedronauck
652
110k
kneath
219
15k
nonsquared
81
3.3k
sugarenia
233
840k
hursman
106
9.2k
Transcript
QZUFTU IFMQTZPVXSJUFCFUUFS %KBOHPBQQT "OESFBT1FMNF %KBOHP$PO&VSPQF 4MJEFTIUUQTQFBLFSEFDLDPNQFMNF personalkollen
5PQJDT w5FTUJOHJO%KBOHPPWFSWJFX w*OUSPEVDUJPOUPQZUFTU w%KBOHPBOEQZUFTU w5FTUPSHBOJ[BUJPO w'BTUFSUFTUSVOT wQZUFTUpYUVSFT
5FTUJOHJO%KBOHP
BGVMMGFBUVSFE1ZUIPOUFTUJOHUPPM QZUFTU
None
1MVHJOT w%KBOHPQZUFTUEKBOHP w%JTUSJCVUFEQBSBMMFMJ[FEQZUFTUYEJTU w-PHDBQUVSFQZUFTUDBQUVSFMPH w$PWFSBHFSFQPSUJOHQZUFTUDPW wBOENBOZNPSF wIUUQQZUFTUPSHMBUFTUQMVHJOTIUNMFYUFSOBM QMVHJOT
XJUIPVUCPJMFSQMBUF 1ZUIPOJDUFTUT
%FNPUJNF
%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'))
%FNP"GUFS def test_hello_world(client): response = client.get('/')
! assert response.status_code == 200 assert 'Hello' == response.content.decode('utf-‐8')
%FNPUJNF w"TTFSUJPOTXJUIBTTFSUTUBUFNFOU w/P5FTU$BTFTVCDMBTTJOH wQZUFTUpYUVSFTGPSUFTUEFQFOEFODJFT
*OUFHSBUJPOCFUXFFOQZUFTUBOE%KBOHP QZUFTUEKBOHP
*OTUBMMTQZUFTUEKBOHPBOEQZUFTU pip install pytest-‐django
"EETZPVSQSPKFDUUP1ZUIPOQBUI SFRVJSFTTFUVQQZUPCFQSFTFOU pip install -‐e .
"WFSZNJOJNBMTFUVQQZ from setuptools import setup setup(name='yourproject', version='1.0')
$POpHVSFTFUUJOHTJOQZUFTUJOJ [pytest] DJANGO_SETTINGS_MODULE=settings
%KBOHP5FTU$BTFT KVTUXPSLT
3VOOJOHUIFUFTUT
3VOBMMUIFUFTUT $ py.test
3VOBMMUFTUTJOBTQFDJpDpMF $ py.test test_views.py
3VOBMMUFTUDBTFTUIBUBSFOBNFE UFTU@GPP $ py.test -‐k test_foo
5FTUPSHBOJ[BUJPO
#ZEFGBVMU UFTUTBSFGPVOEJO test_*.py
$POpHVSBCMFJOQZUFTUJOJ [pytest] python_files = check_*.py
5FTUTDBOMJWFVOEFS%KBOHPBQQT polls/tests/views.py blog/tests/views.py
5FTUTDBOMJWFJOUIFJSPXOSPPUEJSFDUPSZ tests/polls/views.py tests/blog/models.py
5FTUTCFTQMJUVQJOUPEJGGFSFOUSPPU EJSFDUPSJFT unit_tests/blog/models.py integration_tests/test_foo.py browser_tests/test_login.py
None
testing Running tests
5FTUEBUBCBTFSFVTF
3FVTFEBUBCBTFGSPNMBTUSVO py.test -‐-‐reuse-‐db
1VUUIJTJOQZUFTUJOJUPBMXBZTSFVTFUIF EBUBCBTF ! ! [pytest] addopts = -‐-‐reuse-‐db
3FDSFBUFEBUBCBTFTDIFNBXIFOEPJOH NPEFMDIBOHFT py.test -‐-‐reuse-‐db -‐-‐create-‐db
.BLFVTFPGUIPTFFYUSBDPSFT 3VOUFTUTJOQBSBMMFM
YEJTUQSPWJEFTEJTUSJCVUFEUFTUJOH pip install pytest-‐xdist
3VOUFTUTJOGPVSQBSBMMFMQSPDFTTFT $ py.test -‐n 4
QZUFTUEKBOHPBOEEBUBCBTFT
5IJTXJMMBMMPXEBUBCBTFBDDFTT import pytest ! @pytest.mark.django_db def test_user_count():
assert User.objects.count() == 0
5IJTXJMMGBJM EBUBCBTFBDDFTTJTOPU BMMPXFE def test_user_count(): assert User.objects.count()
== 0
! ! ! ___________ 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
.BSLBOFOUJSFNPEVMFGPSEBUBCBTF VTBHF import pytest ! pytestmark = pytest.mark.django_db
! def test_foo(): pass def test_bar(): pass
3VOBMMUFTUTXIJDIEPFTOPUUPVDIUIF EBUBCBTF $ py.test -‐m 'not django_db'
3FQMBDFNFOUGPSVOJUUFTUTFU6QUFBS%PXO QZUFTUpYUVSFT
[{"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
(FUBOJOTUBODFPGUIFUFTUDMJFOU ! def test_with_client(client): response =
client.get('/foo/') assert response.status_code == 200
%FNPUJNF
%FpOJOHBpYUVSF QVUUIJTJODPOGUFTUQZ import pytest from selenium.webdriver import Firefox
! @pytest.yield_fixture(scope='session') def webdriver(): driver = Firefox() yield driver driver.quit()
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'
3FRVFTU%KBOHPEBUBCBTFBDDFTT JOBpYUVSF import pytest ! ! @pytest.fixture def
person_in_db(db): return Person.objects.create( name='Andreas')
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
.PSFpYUVSFTGFBUVSFT w1BSBNFUSJ[BUJPO w4DPQJOH SFVTFFYQFOTJWFTFUVQ w"VUPVTFpYUVSFT
8PSLTXJUICPUI XPSMET
(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
&NBJMBOESFBT!QFMNFTF 5XJUUFS!BOESFBTQFMNF *3$QZMJCPO'SFFOPEF 4MJEFTIUUQTQFBLFSEFDLDPNQFMNF 2VFTUJPOT