.F
w $PGPVOEFSEFWFMPQFSBU1FSTPOBMLPMMFO
w 1ZUIPOQSPGFTTJPOBMMZTJODF
XSJUJOHUFTUT
TJODF
Slide 3
Slide 3 text
5FTUJOHJO%KBOHP
Slide 4
Slide 4 text
5FTU$BTFIJFSBSDIZ
Slide 5
Slide 5 text
No content
Slide 6
Slide 6 text
.VMUJQMFUFTUpMFT
# Typical contents of app/tests/__init__.py:
from .test_foo import *
from .test_bar import *
from .test_spam import *
Slide 7
Slide 7 text
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
QZUFTUEKBOHP
w *OUFHSBUFTQZUFTUBOE%KBOHP
w .BOBHFTUIFUFTUEBUBCBTF
w "MMPXTEBUBCBTFSFVTF
w 1SPWJEFT%KBOHPUFTUIFMQFSTJOBQZUFTUXBZ
w $IBOHJOHPGTFUUJOHT
w -JWF4FSWFS
w VSMDPOGPWFSSJEFT
w *OWPLFEWJBpy.testOPUmanage.py
test
Slide 12
Slide 12 text
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
Slide 13
Slide 13 text
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
Slide 14
Slide 14 text
&YJTUJOHUFTUTVJUF
w %KBOHP5FTU$BTFBOEVOJUUFTU5FTU$BTF
TVCDMBTTFTJTQJDLFEVQBOESVOBVUPNBUJDBMMZ
Slide 15
Slide 15 text
)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'
5FTUBCMF%KBOHPDPEF
w %BUBCBTFBDDFTTNBLFTUFTUTTMPX
w %KBOHP`T03.BOENPEFMTJTHSFBU
w 5IFEJTUJODUJPOCFUXFFORVFSZDPEFBOE
BQQMJDBUJPODPEFDBOCFCMVSSZ
w 1VURVFSZDPEFJONBOBHFSNFUIPET
w 5FTUUIPTFXJUIUIFSFBMEBUBCBTF
w 1VUBQQMJDBUJPOMPHJDJONFUIPETXIJDIEPFTOPU
UIFRVFSZUIFEBUBCBTF
w 4UVCCJOHNPDLJOHDBOIFMQXJUIJTPMBUJPO
Slide 18
Slide 18 text
5FTUBCMF%KBOHPDPEF
w ,FFQWJFXTWFSZTMJN
w 1VTIMPHJDUPGPSNT
NPEFMNFUIPETBOE
VUJMJUZGVODUJPOTUIBUDBOCFUFTUFEJO
JTPMBUJPO
w "WPJECSBODIFTJOWJFXTXIFOQPTTJCMF
w 5FTUWJFXTXJUIBDPVQMFPGIJHIMFWFMUFTUT
XJUIGVMMEBUBCBTFBDDFTT
w 4FMFOJVNBOEXFCUFTUBSFHPPEUPPMT
Slide 19
Slide 19 text
5FTUBCMF%KBOHPDPEF
w %PO`UCFTDBSFEPGDSFBUJOHPUIFSNPEVMFT
FYDFQUWJFXTQZ
NPEFMTQZ
w 5IFNPSFPGZPVSBQQMJDBUJPOUIBUEPFTOPU
EFQFOEPO%KBOHPUIFCFUUFS
w .BLFVTFPGUIF1ZUIPONPEVMFTZTUFN JUJT
SFBMMZHPPE
Slide 20
Slide 20 text
No content
Slide 21
Slide 21 text
%BUBCBTFVTBHF
w 8JUIQZUFTUEKBOHP
ZPVNVTUCFFYQMJDJUBCPVU
EBUBCBTFBDDFTT
w %BUBCBTFBDDFTTJTSFRVFTUFEXJUIUIF
pytest.mark.django_dbNBSLFS
Slide 22
Slide 22 text
%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
Slide 23
Slide 23 text
%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
Slide 24
Slide 24 text
5FTUEBUBCBTFSFVTF
w 5FTUTTIPVMESVOBHBJOTUUIFSFBMEBUBCBTF
w 5BCMFDSFBUJPOUBLFTUJNF
w QZUFTUEKBOHPQSPWJEFTUIF-‐-‐reuse-‐dbPQUJPO
w 1VUaddopts
=
-‐-‐reuse-‐dbJOQZUFTUJOJ
w 1FSTJTUUIFUFTUEBUBCBTFCFUXFFOSVOT
w 8IFOVTJOH-‐-‐reuse-‐db
QBTT-‐-‐create-‐dbUP
GPSDFSFDSFBUJPOPGUIFEBUBCBTF
Slide 25
Slide 25 text
.PEFMUFTUEBUB
w )PXUPHFUNPEFMJOTUBODFTGPSUFTUT
w $SFBUFPCKFDUTNBOVBMMZ
w %KBOHPpYUVSFT OPUQZUFTUpYUVSFT
w 'BDUPSJFT
'BDUPSJFTGBDUPSZ@CPZ
w )FMQFSGPSDSFBUJOHUFTUPCKFDUT
w 4VQQPSUTBVUPNBUJDBMMZDSFBUJOHSFMBUFENPEFMT
w .BLFTNPEFMUFTUEBUBNBJOUBJOBCMF
w 0WFSSJEFUIFTQFDJpDBUUSJCVUFTUIBUBSF
JOUFSFTUJOHJOBUFTU
w $BOCFVTFEJOJOUFHSBUJPOUFTUTBOEVOJUUFTUT
Slide 28
Slide 28 text
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)
Slide 29
Slide 29 text
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'
Slide 30
Slide 30 text
QZUFTUpYUVSFT
w /PUUPCFDPOGVTFEXJUI%KBOHPpYUVSFT
w "XBZPGDPOTUSVDUJOHUFTUPCKFDUTBOEUFTU
EFQFOEFODJFT
w 4JNJMBSUPTFU6QUFBS%PXOCVUNPSFFYQMJDJU
BOENPEVMBS
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
Slide 33
Slide 33 text
'BDUPSZpYUVSFT
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
Slide 34
Slide 34 text
'BDUPSZpYUVSFT
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
Slide 35
Slide 35 text
(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