bsdbox

unit testing with pytest—not easily ignored

<2019-07-20>

Days 10–12 of the 100 Days of Code TalkPython course is dedicated to unit testing with pytest, and is a foison of information. Prior to this, I had very little experience with pytest but found it less intuitive than the language itself, and somewhat obscure—at least at first. I think, however, this was because of trying to concinnate the actual tests—much like I would any program—rather than simply hardcoding the input and expected output of a function's given test with the parametrize decorator. Instead, I would essentially try to rewrite the function logic differently to reproduce the desired behaviour. This, however, is counterintuitive because I'm introducing another possibility for faulty logic, albeit in the test, so if the tests pass but both models are flawed, I'd never know. On top of which, I was testing a command-line driven app, so figuring out how to write tests for sys.argv input required additional understanding—and I'm still not entirely sure I've written the most efficient tests but I did achieve complete coverage so I'm satisfied with the result. Nonetheless, what I want to share is the apparent discrepancy between the advertised way to ignore or exclude directories in your project repository, and what's actually needed to get it done.

Not as easy as advertised!

First, the pytest docs suggest that pytest will "intelligently identify and ignore a virtualenv"—but this wasn't happening for me. So I tried the norecursedirs in my setup.cfg to no avail. Next, I took a chance with collect_ignore and collect_ignore_glob in my conftest.py. This, too, came up short. I then thought I'd settle for invoking pytest with the --ignore option and was duly mystified when that proceeded to fail. Unwilling to accept defeat, I returned to my setup.cfg, drew up a coverage:run] section, set an omit option, and alas—it worked! Here's what you need in your setup.cfg if you run into a similar situation:

[coverage:run]
omit =
     .env/*

Save to your project root, and pytest will omit .env directory contents from testing. Obviously, you can specify any number of locations, and replace .env with your Python virtual environment directory name.

Tangentially, if—like me—you're obstinately chasing 100% coverage, you can add the following to circumvent the __main__ Pythonic problem:

[coverage:report]
exclude_lines =
     if __name__ == .__main__.:

If not already using pytest, I recommend trying it with a personal project. Some benefit may be derived from this integral component of test-driven development. Besides which, many software development life cycles incorporate testing, so the experience could prove useful.

pytest coverage report
Figure 1: pytest coverage report
Tags: code python
send comments to mark AT jamsek DOT net

Generated by emacs org mode

Copyright © 2023 Mark Jamsek