Tests and code coverage in Python

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:

pytest -v

Screen Shot 2018-12-11 at 9.33.36 PMWith 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 ./

Screen_Shot_2018-12-11_at_9_44_29_PM.png

“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:

Screen_Shot_2018-12-11_at_10_04_37_PM

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

[report]
exclude_lines =
pragma: no cover
@abstract

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.

 

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.