Not only is Python a nice language but it has always had a lot of tooling around it. I’ve always taken advantage of Python’s tooling around testing (okay, not always …) and recently I began to pay attention to code coverage again. Python makes it all so simple and delicious.
I have used nose in the past and now I’m taking pytest for a spin (I do like pytest intrinsically, but, I have to add, web searches for “pytest” are a lot more productive than websearches for “nose”). I just create a folder test and write up my tests there. I’m not going to use the word TDD but I have found great satisfaction in writing out the test suite in parallel or in advance of the code.
All of us probably ad hoc test our code anyway, especially in a REPL-centric language like Python. Instead of wasting all that effort, just add a little discipline and write down those ad hoc tests in a file – boom! You have a test suite. And I attest to all the benefits of such a discipline: it serves as example usage of your code, it guards against regressions, it gives you confidence to aggressively refactor your code, it makes you think harder and smarter about the structure of your code – namely how to make it more modular and compartmentalized, extracting out the logic from the I/O and from the UI. And the joy of getting that little green “pass” bar to fill up over time can not be underestimated:
With the –pdb option pytest also allows you to break into the debugger at the point a test fails. Pretty useful and amazing.
When you add coverage into the mix it gets even more fun. pytest has a plugin for Python’s famous code coverage module
pip install pytest-cov
and then all you do is
py.test --cov=mymodule ./
“Now, how do I know which lines of code I haven’t captured in my tests?” Funny you should ask. pytest’s coverage module has an html report mode:
py.test --cov=mymodule --cov-report html ./
Which produces a sheaf of html reports, one page per file, that look like this:
The report highlights lines that were never executed during the tests.
“How do I exclude abstract base classes and some genuine junk I have in my code base from being included in the coverage report?” Well, pytest-cov has you covered. You just create a .coveragerc file like so
pragma: no cover
with directives to coverage what to exclude and so on.
With tools like this there is very little excuse not to have a test suite and periodic assessment of your test suite coverage.