[Tutor] Do not understand code snippet from "26.8. test — Regression tests package for Python"
Martin A. Brown
martin at linux-ip.net
Sun Apr 16 16:59:25 EDT 2017
Greetings boB,
>2) The big question: What is the program flow for this program? I
>am not seeing the order of execution here. How is the unittest module
>handling the execution of this?
This is a very good question and one that was (at one time)
inobvious to me, as well.
When you write a program, you usually have a clear notion of where
the program will begin and then can follow its execution path (from
"if __name__ == '__main__'") through your functions, class
instantiations and method calls. This is the execution structure of
your program. (I'd imagine you have used print and/or logging to
debug program flow, as well....)
Testing flow is different.
The sequence of test execution has nothing to do with your program
structure. This is utterly intentional.
[ background / digression ] By breaking down a complex program into
smaller testable pieces, you can have more assurance that your
program is doing exactly what you intend. Since you are breaking
the program into smaller pieces, those pieces can (and should) be
runnable and tested without requiring any of the other pieces.
Usually (almost always) tests are run in isolation. This allows you
to control exactly the arguments, environment and conditions of each
test.
You may know most of the above already, but I repeat it because
these facts help explain why testing tools work as they do...
[ end digression ]
Now, to the unittest module.
(Please note, I'm not an expert on unittest internals, so I may get
a detail or two wrong. Nonetheless, I hope that my answer will help
you orient yourself around what's happening.)
When you run a tool to collect your tests and execute them, the path
through your pieces of code under test has no connection whatsoever
to process flow through your program.
The basic flow looks like this:
* find the test cases / test suites you have written
* run each of the tests independently, i.e. isolated conditions
* report on the success/failure of each test case, test suite and
the whole batch
See below for more detail of the mechanics inside the unittest
module. What happens when you execute your testing suite? Let's
say you run:
$ python -m unittest
(Unless there is customization of TestLoader TestSuite and/or
TestRunner) the following sequence occurs:
1. the Python interpreter starts up
2. Python loads the unittest module and passes control to unittest
3. unittest.main creates an instance of unittest.TestLoader [0]
4. unittest.TestLoader scans the filesystem, collecting a list of
tests to run from:
- any test suites subclassed from unittest.TestSuite [1]
- any test cases subclassed unittest.TestCase [2]
5. unittest.TestLoader imports anything it found and returns the
list of tests to the main testing program loop
6. the main loop passes the tests to the unittest.TextTestRunner [3],
which executes each test and (probably) produces some output
telling you either that your hard work has paid off or that
something is still wrong
Your next question is why do the mixins work? And how do they work?
I'll make a few observations:
- [on unittest] the unit testing tools use classes because it's a
natural way to accommodate the goal of reproducibly setting up
arguments and/or an environment for each test (note that each
TestCase can have its own setUp() and tearDown() methods; this
allows isolation)
- [on unittest] each test collected by the TestLoader can be any
Python class (as long as it is also derived from
unittest.TestCase)
- [on your classes] your classes use a multiple inheritance
model, deriving from TestFuncAcceptsSequencesMixin; when
instantiated, they'll have all of the expected TestCase methods
and the method called 'test_func'
In more detail, you have created three different classes, each of
which is derived from unittest.TestCase (I'm showing just the
signatures):
class AcceptLists(TestFuncAcceptsSequencesMixin, unittest.TestCase):
class AcceptStrings(TestFuncAcceptsSequencesMixin, unittest.TestCase):
class AcceptTuples(TestFuncAcceptsSequencesMixin, unittest.TestCase):
Here's what's happening:
- TestLoader finds the files that contains the above classes (probably
named 'test_something.py')
- Testloader imports the file 'test_something.py'; this defines your
classes: AcceptLists, AcceptStrings and AcceptTuples (or will
produce a traceback if the code does not import; try breaking
your code and you should see that the import of your test code
fails during the TestLoader phase)
- TestLoader appends the now-defined classes: AcceptLists,
AcceptStrings and AcceptTuples to the list of tests
- control passes back to main and then to TestRunner
- for each unittest.TestCase in the list of tests, TestRunner will:
- create an instance T from the defined class
- for each method name starting with 'test_' (you have only
'test_func') TestRunner will:
- execute the T.setUp() method if it exists
- TestRunner will execute the method 'test_func'
- collect the success / failure and any outputs
- report on the success / failure
- produce some final summary output and set the exit code
accordingly (os.EX_OK means success, anything else is failure)
>The ending comment in the docs' example cited reads:
>
>"When using this pattern, remember that all classes that inherit from
>unittest.TestCase are run as tests. The Mixin class in the example
>above does not have any data and so can’t be run by itself, thus it
>does not inherit from unittest.TestCase."
>
>This suggests to me that unittest "uses" the bottom three classes, but
>even though each of the three inherits from the class
>TestFuncAcceptsSequenceMixin, those classes don't have any methods
>that they call on that class, so how does its code get run? I suspect
>that the mixin's concepts is where I am stumbling. I have yet to find
>a reference that is making things clear to me, though I will continue
>searching and reading.
I hope my long-winded explanation amkes that a bit clearer.
Good luck,
-Martin
[0] https://docs.python.org/3/library/unittest.html#unittest.TestLoader
[1] https://docs.python.org/3/library/unittest.html#unittest.TestSuite
[2] https://docs.python.org/3/library/unittest.html#unittest.TestCase
[3] https://docs.python.org/3/library/unittest.html#unittest.TextTestRunner
http://www.drdobbs.com/testing/unit-testing-with-python/240165163
--
Martin A. Brown
http://linux-ip.net/
More information about the Tutor
mailing list