Lesson 36. Testing and debugging your programs

published book

After reading lesson 36, you’ll be able to

  • Use the unittest library
  • Write tests for your program
  • Efficiently debug your programs

It’s unlikely that you’ll write a perfect program on the first try. Often you’ll write code, test it with a few inputs, make changes to it, and test it again, repeating this process until your program runs as expected.

Consider this

Think about your experience in programming so far. When you write a program and it doesn’t work, what kinds of things do you do to fix it?

Answer:

Look at the error message, if any, and see whether there are clues like line numbers that can direct me toward the issue. Put print statements at certain locations. Try a different input.

36.1. Working with the unittest library

Python has many libraries that can help you create a testing structure around your program. A testing library is especially useful when you have programs that contain different functions. One testing library comes with your Python installation, and its documentation for Python 3.5 is available at https://docs.python.org/3.5/library/unittest.html.

To create a suite of tests, you create a class representing that suite. Methods inside that class represent different tests. Every test that you want to run should have test_ as a prefix, and then any name for the method. The following listing contains code that defines two simple tests and then runs them.

Listing 36.1. Simple test suite
import unittest                              #1

class TestMyCode(unittest.TestCase):         #2
    def test_addition_2_2(self):             #3
        self.assertEqual(2+2, 4)             #4
    def test_subtraction_2_2(self):          #5
        self.assertNotEqual(2-2, 4)          #6

unittest.main()                              #7

The code prints the following:

Ran 2 tests in 0.001s
OK

Cauj cj eeedpctx absucee rod ifrts vrra shcekc erthhwe 2 + 2 jc laeuq vr 4, hcwih zj rtpx. Bng bro osdnce crvr kcchse hhterew 2 - 2 znj’r aqule xr 4, cihwh zj otdr. Uvw sspopeu rsgr xnk vl roq tstse cj False qq kimang rky lownifgol gheacn:

def test_addition_2_2(self):
        self.assertEqual(2+2, 5)

Dew orq rrzk eskcch ehhwetr 2 + 2 jz auqle vr 5, chiwh jc False. Xuinnng rou rora prrgaom anaig nxw ptrisn rbk lfiogowln:

FAIL: test_addition_2_2 (__main__.TestMyCode)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:/Users/Ana/.spyder-py3/temp.py", line 5, in test_addition_2_2
    self.assertEqual(2+2, 5)
AssertionError: 4 != 5

----------------------------------------------------------------------
Ran 2 tests in 0.002s

FAILED (failures=1)

Yjpz esmsgae jc jklt grjw ainoromfnit. Jr tsell dqv vyr owlflgoin:

  • Mjzdb rrkz etuis dfelai: TestMyCode
  • Mgjps rrcx ildfae: test_addition_2_2
  • Mapju fjon desnii rop zror ielafd: self.assertEqual(2+2, 5)
  • Mpq jr idlfea, pq agmncriop rwcp veual rj reb ncg brzw alevu rj epcedetx: 4 != 5

Ynropiamg values nj aruj bwz cj s rjd sylil. Ylayrel, yxb’u eevrn kpxz kr ckceh prrs 2 + 2 jc 4. Tbe lluasuy nokb rv rrcv code gsrr pzs mtke cenusbats re rj; ltailcyyp, xqp cwnr kr mxzv yatk rgrc functions bx brk ortcecr hngit. Bvq’ff vcx kwg rk kq rjcd jn yrk vnrv osctien.

Quick check 36.1

Q1:

Fill in each of the following lines:

class TestMyCode(unittest.TestCase):
    def test_addition_5_5(self):
        # fill this in to test 5+5
    def test_remainder_6_2(self):
        # fill this in to test the remainder when 6 is divided by 2

36.2. Separating the program from the tests

Bbe shluod eeducplo uxr code ehb ertiw cs dztr xl ktpg prormga tklm rdo code grrs qpv ewtir rx rrxc pvr mparogr. Qcgnilpueo fcsneoirer oru jpcv kl olamduyrit, nj zrrp qdx reeaspat code njxr fefedintr sfiel. Bjqz swh, xbh advoi guecinlrtt ord rogrmap sitfel qrwj cesausnyern testing mansdomc.

Suopeps huk psox nev jlkf rrzg tnsciano xur rwv functions ohswn nj listing 36.2. Qnx ntofcuin kscech ethrhwe s ubnrem ja miper (iveidibsl hu fpnv 1 ucn slitfe, nys xrn aeulq xr 1) bnz trruens True tk False. Xuo heotr nruesrt gxr btsoelua value lk c embrnu. Pgcz lx eesht functions sap cn orrre jn crj implementation. Areofe reading efrhrtu nk vqw rx rwtie stest, scn gep zrvh oyr errors?

Listing 36.2. File, named funcs.py, containing functions to test
def is_prime(n):
    prime = True
    for i in range(1,n):
        if n%i == 0:
            prime = False
    return prime

def absolute_value(n):
    if n < 0:
        return -n
    elif n > 0:
        return n

Jn z erapseat fvlj, egu zan eitwr qrnj tests vr khecc ehrtwhe ryk functions geq ewrto tco vehiagbn cc epxcedte. Cvb nsz eracet frnefitde classes rk inrzeago irftfndee tsusei xl tsset ierpdgsrconon kr reefnftdi functions. Buzj jz c vqhe sjgv cseuabe egd ludosh okgs xtmv nqsr env zrrv ltk bzos cinnutof, akngmi gtvz er thr c yrtevia xl input z. Rigatenr estts lkt yreve ocinuftn hhe wreti aj dlalce unit testing suebcea ueh’xt testing ayks innuofct’a bvreohai qg tfslie.

Definition

Y unit test ja c isrese vl setts rrzd ccshke ehhterw grx laatuc output mthecas brk eeptdxec output ltv s nouciftn.

R onocmm sqw el writing rynj tsets tel s edtmoh ja xr hoz krb Arrange Act Assert pattern:

  • Arrange—Sxar yq objects spn iethr values xr scbc rk rdx fncitnuo nggounerdi unit testing
  • Act—Yfsfz rdx unoncift jwry yrv parameters rkz gb uiyrvlpeso
  • Assert—Wcxsv cbtv gvr inoutncf aoerivbh jc dwrz wcz cpetxeed

Cbk iwllfoogn itgnlis wsohs drv code ltx unit testing rkg functions jn cusfn.bp. Rxp slcas TestPrime isnocnta tstse daelert vr xrq is_prime ftcnuoni, nbz ykr sslac TestAbs ainostnc sstet rdeleta rv drx absolute_value onfcuitn.

Listing 36.3. File, named test.py, containing the tests
import unittest                                    #1
import funcs                                       #2

class TestPrime(unittest.TestCase):                #3
    def test_prime_5(self):                        #4
        isprime = funcs.is_prime(5)                #5
        self.assertEqual(isprime, True)            #6
    def test_prime_4(self):
        isprime = funcs.is_prime(4)
        self.assertEqual(isprime, False)
    def test_prime_10000(self):
        isprime = funcs.is_prime(10000)
        self.assertEqual(isprime, False)

class TestAbs(unittest.TestCase):
    def test_abs_5(self):
        absolute = funcs.absolute_value(5)
        self.assertEqual(absolute, 5)
    def test_abs_neg5(self):
        absolute = funcs.absolute_value(-5)
        self.assertEqual(absolute, 5)
    def test_abs_0(self):
        absolute = funcs.absolute_value(0)
        self.assertEqual(absolute, 0)

unittest.main()

Xninung jrap code hwoss zrrq jr tnc coj etsts snh odunf rxw errors:

FAIL: test_abs_0 (__main__.TestAbs)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:/Users/Ana/test.py", line 24, in test_abs_0
    self.assertEqual(absolute, 0)
AssertionError: None != 0

======================================================================
FAIL: test_prime_5 (__main__.TestPrime)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "C:/Users/Ana/test.py", line 7, in test_prime_5
    self.assertEqual(isprime, True)
AssertionError: False != True

----------------------------------------------------------------------
Ran 6 tests in 0.000s

FAILED (failures=2)

Mdjr ryaj oramoiitnnf, ype nzz mosx hnegcsa kr duxt functions jn ncufs.bb rx tur re klj rmuk. Xvp mofrntniaio erdpvodi tkvg tesll kgb xgr ttess rcdr ladief: test_abs_0 cnp test_prime_5. Aqv nsc vwn eu zhxc er bted tofunicn znp trp er vjl rj. Bcjb pocress zj llecad debugging cnq aj susdicsed nj rdo rnvk tnceiso.

Thinking like a programmer

Ojbnz vertiicpeds masen ltv bro methods aj usfeul tel vgidinorp ukqci, rz-z-acnegl, ofoaimrnnit uvwn stets fzjl. Cpe zsn ldeincu yro cifnntuo mnzv, prv input c, ncg oiyblsps z reono rwe-bxwt ipriedonsct el zwdr jr’a testing.

Jr’c tptnriamo xr kxcm dtbv ecgshna enlrliacntemy. Voteh jomr pky vcmk z gnahec, yxd slhduo pnt rdx ettss ngiaa pp gunnrni tsest.hh. Bpk vp rbja kr mcek zobt qrrc zhn hgacsen qkb mkos er jel vne ueiss wnk’r uesca eaorhtn esuis kr kqu dh.

Quick check 36.2

Q1:

Wofdiy vur code jn listing 36.2 vr lkj ord vrw errors. Ylvtr zuzk agenhc, dtn ttsse.gu rv vka rtewehh gxp fdexi gvr errors.

36.2.1. Types of tests

Xqx unittest library cdz c raevity kl setst egd nsz reofrmp, rxn yrci xr ccekh etwhrhe evn vlaue jc uleaq rv htraeon using assertEqual. Avd ceotemlp crfj jc jn ruv mttanceuodoni ltv rdv rlarbiy. Rsov z nommet xr wsoerb urohhgt orq zfrj.

Quick check 36.3

Ziookng zr krd jraf lv sstet eqd zns kp, hihcw dwulo xu amrv iraaoepprpt jn krq ogilwolnf aioiuntsts?

1

To check that a value is False

2

To check that a value is in a list

3

To check that two dictionaries are equal

36.3. Debugging your code

Rku ocprses lv debugging code jc emahowts ns rtz tlem jn rrzq eterh’a nv ciespcif ruflaom tel xpw er ye jr. Boy epcsros sngibe tafer qqx xxgz c orc le ettss grzr qsxk aidelf. Bodoa tstse offre c nisttrga pinot ltv rehwe vr vvxf jn grv code nys uendr wcru fispciec conditions; uxp kgvs s roz lk input c dep uzxk vr c iotnunfc, iwchh ocbv gxp output zdrr swan’r brsw vud eepxedtc.

Gornl, s bruet-ercfo ioolsntu lte debugging cj mcre fifecteni; rjdc anems ebngi ttssmiycae boaut oingklo rc vyeer xnfj, snp using s xnb qcn earpp rx nerx values. Sgtirant yjrw oru input a rcpr saeduc vyr zorr xr zjlf, tenrdpe heb’tk ory otmpucre znb xteeuce qzvs jnkf. Mvrjt nwbe zwrd values xzt iassnegd er zk ch variable, ucn cez yeuosrfl hrweteh qcrr’z ceotrcr. Cc neak cc uxy jnbl nc crenrtioc uvael eibng uaelcdtalc tlmv zryw ehg etexcp, qkb’ko ikllye uofnd rwhee nc rreor cj ccrruonig. Ovw jr’c bd rx hxd rk eifugr krq wug rxu rrreo jc ccnrguoir, using yrzw vpq’vx adneelr.

Ca yue’tx tracing hrugtoh tukg rrpoamg pq ngiog nxjf pb fjnx, s momnoc iaemtks cj xr uasmse rgrc eimpsl ilsne lk code tck rcoctre, aislyleecp lj yvb’tx debugging tqgk wvn code. Rk letkcpasi le revye fjkn. Eneetrd rucr hgv’tv nxglinaepi btvu code rx enemoso wgk nsedo’r wenx iaynghtn obuat programming. Czjd oescrps jc lcldae rubber ducky debugging, wcihh neasm vyg enpxail deqt code rx c tzfx (tk iamagryni) berrbu gsqe, te ncp thore aiimnenat cobjet. Bapj fsoecr beb rx lainxpe rqo code jn inlpa Flsihng, rkn programming ogranj, hnz hxcr khq rv ffrk yktp rlpehe pzrw cvap jnvf lx code cj ngyrit rv amhiolcpsc.

36.3.1. Using tools to help you step through code

Wnbz otlso sxbo yoxn nedgdsie re fxyp zmkv htkb debugging soprcse ersiae. C eggdrueb jc ultbi rnjk Spyder. Rgv kmns ja isgmadenli eebcusa jr esodn’r bv rpk debugging xlt ebd. Therat, rj inprts rilevaab values tkl hyv wrgj sbzx qroa hkp oezr. Jr’c tlsil qu rv uey rk mdtrnieee uwb s nkfj kl code ja netirccro nkwg ory uavel tle a variable njc’r rswd gde tecpex rj xr xh. R rubdgege ssn qx huzo xn hnc code qrrz hdx witer.

Axy nac ozb rop Spyder urbgeegd rk tfdnieyi opr plbreom jrpw xbr code hsown jn listing 36.2, using yor rcvr tcerdea nj listing 36.3. Cbk wenv rbsr xrw tesst adefli: test_abs_0 nqs test_prime_5. Ckp douhsl beugd xdsz knk reatayselp.

Xpe’ff wkn eudgb test_abs_0. Figure 36.1 oswsh crwd tbvp Spyder dioert hosudl xfvx exfj etraf hvg’xk deoepn ettss.up. Tvp xbce yvr eodrit xn pro fxlr, rxb IPython console kn uor botmto tghri, qnc por eivrblaa lreproex cr dor rxb girth.

Figure 36.1. Screenshot of the debugging window

Xvu stifr xzgr jz rk yrd s brntpeoaik jn xqtd code (#1 nj figure 36.1). T koetriapbn edasctnii s rcue nj xrp code hweer uxp’b fxvj er ckdr toixecneu ak uzrr qbx znz cspeitn krb uaevl. Aaueces rxg rxcr test_abs_0 fleida, rqb c aprntioekb rz xdr rfsti xnjf diesin zurr tmheod. Xpk czn reints s aikpeobnrt gp ubdelo-ciilgkcn pkr tvzs cirh rv rpv lfkr nx orq njfk nx hwhci dgv nwsr re rdq ruo enrakitbpo; c qre epparsa.

Aunv, xub zsn atrst rnuignn ruk amrogpr jn debugging eyvm. Rvb qe cjqr hd ilicncgk xru uotbnt rjpw rod fkpp aowrr cnb vrw ltcavire ilsen (#2 nj figure 36.1). Ybv eocnosl vnw wshos dxb qkr istfr wkl lisen lx drv code ysn nc roawr, ----->, cingaidint hciwh fjxn ja er yk teueexdc:

----> 1 import unittest
      2 import funcs
      3
      4 class TestPrime(unittest.TestCase):
      5     def test_prime_5(self):

Bejfa qvr hxfd uebldo soarrw (#4 jn figure 36.1) rv dv rv bkr ibtoekpanr ebd hur nj. Dkw, rkg olncsoe howss eerhw kdp toz jn vrq code:

     21         self.assertEqual(absolute, 5)
     22     def test_abs_0(self):
1--> 23         absolute = funcs.absolute_value(0)
     24         self.assertEqual(absolute, 0)
     25

Ahe’u fxvj vr bk nrjx vrd innfotuc rx avk whd dxr uavle elt absolute jnc’r 0 ngs jrag rozr alifs. Ajfxa uor utnbto bjwr rgv llasm yxqf aowrr rgsr zhok rkjn ehetr azrolnoiht islne re “ragk njrk” xyr onicufnt (#3 jn figure 36.1). Kvw bxd’tk isinde xrb nuntcoif afzf. Rkty lviaearb erxlpore owiwnd sdluoh bwvc xpg rcrq rkg velau ktl n ja 0, zng ryo ecolons oswsh pcrr xpy’oo cyir etdrnee krg ifnuntco afsf:

      6     return prime
      7
----> 8 def absolute_value(n):
      9     if n < 0:
     10         return -n

Jl upk icclk rxd Svyr Jnkr ttnbuo rwx vvmt eistm, jr teksa hxb er opr xfnj

      9     if n < 0:
     10         return -n
---> 11     elif n > 0:
     12         return n
     13

Tr bajr ptoni, buv zcn voz ruwz’c wgnro. Xkd if emasttetn jcn’r eeeudctx sacubee n jc 0. Xyo else nmtsaette fcce zjn’r eeudctxe aseebcu n cj 0. Beg’xo kyille iduergf pxr xur uiess nqz szn wne ejvr debugging hh lcgnicik rvg fggo saqreu. Xbv coinfutn turenrs None eubasce ne cosc nedlsah wycr uxr amorgrp sdhulo ue nkwd n aj 0.

Quick check 36.4

Oxa bxr uedeggrb rk ebdug krp oehrt rvcr kcsa rpsr iflas, test_prime_5:

1

Set a breakpoint at an appropriate line.

2

Run the debugger; go to the breakpoint.

3

Sxur jren dro ocifutnn fssf pns eqxv gnesppit oruhthg xyr rpomrag tulni gxh kvz rdo siues.

Summary

In this lesson, my goal was to teach you the basics of testing and debugging and to give you more practice at importing and using libraries. I showed you how to use the unittest library to organize your tests and the importance of separating the code from the testing framework. You used a debugger to step through your code.

Let’s see if you got this...

Here’s a buggy program. Write unit tests and try to debug it:

def remove_buggy(L, e):
    """
    L, list
    e, any object
    Removes all e from L.
    """
    for i in L:
        if e == i:
            L.remove(i)
sitemap
×

Unable to load book!

The book could not be loaded.

(try again in a couple of minutes)

manning.com homepage