[Tutor] Unit testing in Python (3.3.0) for beginners

Amit Saha amitsaha.in at gmail.com
Sun Dec 8 13:25:16 CET 2013


On Sun, Dec 8, 2013 at 8:22 PM, Rafael Knuth <rafael.knuth at gmail.com> wrote:
> Hey there,
>
> I struggle to understand what unit testing specifically means in
> practice and how to actually write unit tests for my code (my gut is
> telling me that it's a fairly important concept to understand).

Your gut feeling is right. However, you will only *truly* understand
it's usefulness as you write more programs yourself. No matter how
many articles or blog posts or books you read it in, it's something
which you will yourself have to "realize" and once you do so, you will
find great value in it. Also, don't fret if you don't realize it. You
will, sooner or latter.  Here is a fairly common use case where you
will really find tests useful (try it!):

Say, you have a fairly big program, single file (module) or spread
across multiple files. Now, as you improve your understanding, you
realize that you can write more efficient/elegant code. So you go on a
wholesale refactoring spree (the technical term for changing your
existing code to cleanup your existing code to write more optimized or
idiomatic code). However, you are not very confident that all the
changes haven't broken any of your existing features. So, what do you
do? When you don't have tests, you have to manually imagine and run
the programs for all possible inputs and check if the output/results
are as expected. On the other hand, if you have a tests, you can
simply run these tests and refactor your code confidently. However, I
have also realized during my own learning adventures and learning from
the existing code base and users' bug reports that the benefit of
tests are solely dependent on the the "test cases" you have covered in
your tests. If you missed a scenario (a test case) in your tests, then
it is likely that any breakage of functionality in that area of your
program will not be undetected when you run the tests.

>
> Over the last few days I learned how to write and work with classes, I
> learned quite a lot about functions, nested loops and I currently walk
> through every program in the Python.org wiki "Simple Programs"
> https://wiki.python.org/moin/SimplePrograms ... and here's the unit
> test program they provide:
>
> import unittest
> def median(pool):
>     copy = sorted(pool)
>     size = len(copy)
>     if size % 2 == 1:
>         return copy[(size - 1) / 2]
>     else:
>         return (copy[size/2 - 1] + copy[size/2]) / 2

BTW, this program will not work in Python 3, since a division
operation returns a float. so, 4/2 = 2.0 .

> class TestMedian(unittest.TestCase):
>     def testMedian(self):
>         self.failUnlessEqual(median([2, 9, 9, 7, 9, 2, 4, 5, 8]), 7)
> if __name__ == '__main__':
>     unittest.main()
>
> Also, I went through the "Beginning Test-Driven Development in Python"
> http://net.tutsplus.com/tutorials/python-tutorials/test-driven-development-in-python/
> but I have to admit I still don't fully understand how unit tests work
> in practice and how to write my own unit tests.

I will attempt an explanation with a far simpler function:

# myarith.py
def sum(a,b):
    return a+b

Let's say you have saved this function in a module, myarith.py. Now,
how would you really verify if the function actually returns the sum
of the numbers passed to it? By calling the function of course. Here
is one way:

>>> import myarith
>>> myarith.sum(1, 2)
3
>>> myarith.sum(1, 5)
6
>>> myarith.sum(-11, -5)
-16
test case
So, you are confident that your function works as expected. Now,
instead of calling your function as above, you want to use  the
unittest module. Here is a  test module which tests the above
function:

# test_arith.py

import unittest
import myarith

class ArithTest(unittest.TestCase):

    def test_sum(self):
        self.assertEqual(myarith.sum(1,2), 1+2)


if __name__ == '__main__':
    unittest.main()

We first create a class ArithTest which is a subclass of
unittest.TestCase. This means that, the class ArithTest is a test case
and in it you will have various tests to test the functions in your
myarith module. Right now, there is only one function, sum(). So, we
create a function, test_sum() where we call the sum() function as
above, but we call it inside the assertEqual() method which basically
tests whether the first argument's value is equal to the second
argument. Now, when you run this module, you will see something like:

$ python3 test_arith.py
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK


Now, let us modify the test_sum() method:

import unittest
import myarith

class ArithTest(unittest.TestCase):

    def test_sum(self):
        self.assertEqual(myarith.sum(1,2), 1+2)

    def test_sum_2(self):
        self.assertEqual(myarith.sum(2,2), 15)

if __name__ == '__main__':
    unittest.main()

A new test has been added, test_sum_2() which is obviously going to
fail. Let's see what happens when we run the module again:

.F
======================================================================
FAIL: test_sum_2 (__main__.ArithTest)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_arith.py", line 10, in test_sum_2
    self.assertEqual(myarith.sum(2,2), 15)
AssertionError: 4 != 15

----------------------------------------------------------------------
Ran 2 tests in 0.001s

FAILED (failures=1)


So, it tells us that two tests were ran and one of them failed.

>
> As it turned out over the last few weeks, the best modus operandi for
> me as an absolute beginner is to grab a small program, take it apart
> in the first place, understand how each component works through trial
> & error, then put all those pieces together and then I kind of get the
> big picture. Once I "get  it" I practice as much as possible to
> memorize what I just learned and *then* I start readying as many
> blogs, tutorials etc. as possible to deepen my understanding (I
> frankly find most tutorials & blogs too complex and confusing from a
> beginner's viewpoint, and I learn faster by taking code apart and
> learning through trial & error in the first place). So, what I am
> specifically searching for is a very simple code sample which I can
> take apart and iterate through each component, and I was wondering if
> you are aware of resources that might be helpful?

Does the above example help?

>
> My understanding of unit testing is that I have to embed my code into
> a test and then I have to define conditions under which my code is
> supposed to fail and pass. Is that assumption correct?

Almost. However, as you can see from the above example that you need
not write tests in the same file as the code you are testing. You
define a set of input scenarios and check if your program is behaving
as expected. Sometimes, the behavior is returning the correct value,
and other times, your program should error out gracefully.

If you have been able to understand a little bit more, I would also
suggest you to take a look at CPython's tests for lists:
http://hg.python.org/releasing/3.3.3/file/b34363761e4c/Lib/test/test_list.py
as an example of the kind of things you should look to test and how
they may be done.

>
> I am a bit lost & confused here .. any help & hing is highly appreciated!
>
> Thank you & all the best,

I hope I have been able to make it a little better for you.

Good luck.

Best,
Amit.

-- 
http://echorand.me


More information about the Tutor mailing list