I have now added some testing to my ez-txt2html converter.
I decided to use pytest as framework for running tests on this project. It seems like a popular choice for many Python developers. As I built tests I also took advantage of some of the tools available in the built-in Python module unittest. It’s mock()
function provides some helpful ways of providing mock data in order to run tests on certain functions independently, hard coding variables from calling functions.
Pytest was an easy install, simple as pip install pytest
and running it had a reasonable learning curve. I wrote test (.py) files in my src directory alongside my main module files, running them simply with CLI prompt pytest test_file.py
and responding to any errors or warnings at runtime. I have some work to do to further refine my directory structure, but for now for simplicity on a small program, this was my cleanest approach.
The real work came from thinking through the types of tests needed for various processes and functions. There are a huge amount of variables and outcomes to think through. I found that searching testing examples that involve similar input/output types, common intention or have similar file and code structure is a good way to think of what some of the priority types of tests are. Ultimately it really does take a great deal of prioritization before jumping too heavily into the code.
In writing tests, I learned a lot about how isolate individual functions, and some of the more in-depth python theory around modularity. Thinking about a function being called and receiving explicit parameters when isolated from the main program, causes one to look at how certain functions can be designed differently. Also thinking about how you can assert outcomes that fulfill a tests purpose can be very difficult, especially when a function may interact with global variables or doesn’t return an objector variable?
While creating tests for functions involved in reading from file and converting text content to html, I was able to take a much more detailed look at the related algorithms and identify some output formatting inconsistencies. That would have been difficult to identify were I not going through the individual functions and trying to define what the expected output format should be. the biggest realization about writing tests was how important it is to understand and be able to exactly define the expected outcome/output of a program or function is, was probably the biggest realization about writing test.
Working with test suites, as with many additions relating to accommodating future development and contributions, really causes you to reassess and tidy things like file structure, naming conventions, modularity and so on. I've struggled to a degree with various environment configuration and path related challenges while trying to integrate more new tools, techniques, and workflows. It's a challenge but it's rewarding when you work through it.
Top comments (1)
That's precisely why globals and singletons are a code smell, and implementing program functionality in side effects is an anti-pattern.
Not only is it much, much harder to test functions written that way, but it makes them non-thread-safe and ruins their concurrency. (Thanks to the GIL Python threads aren't literally unsafe even when accessing globals, but if your function's behavior is dependent on the current state of a global, or if it stores a particular piece of data in a global and needs to access it later in the function's execution, then you can't ever run two instances of that function at the same time without them modifying each other's data and altering the results.)
The fact that pytest runs tests concurrently means that testing any functions written that way will very likely lead to test failures when the results of the tests don't match what's expected. While there are ways to work around that sort of thing in pytest, to fix the tests, a much better solution is to fix the code under test so it doesn't work that way anymore. Cleaner tests probably won't be the only benefit.