Now that handlers are explicitly passed in an environment, we can create a test environment. It's surprisingly easy to go wrong with test environments. Here are some failure modes I've seen:
This last is the most interesting to me. First, I think it's underappreciated that an object with too many fields or properties is often ill-designed. Sometimes the domain really is that complicated, but often this is a sign that one code object is representing many logical or real-world objects. That's a problem. Often, homonymy seems to be behind this: the "delivery" as understood in an order-processing system is (arguably) not the same thing as, or at least not best represented as identical to, the "delivery" that is a box you might be monitoring with a smart camera on your doorstop.
Second, lots of helper functions seem to be motivated by a desire to reduce lines of code. Avoiding repetition is nice, but test code is supposed to be a bit repetitive, and readers don't mind scrolling a bit. More fundamentally, sometimes a bespoke set of test data really needs to be created, with a certain amount of variation in various fields. Stuffing all of that variation in as values of a dozen parameters of a
create_foo_test_data function does not make things more readable.
Finally, I've occasionally seen towers of helper functions, like:
The underlying issue seems to be that whatever helper function was useful at a given moment was checked in and made immortal. I find that a few text fixtures, along with ad hoc test data creation, serves me well. I don't mind reading test code that requires a bit of copying and pasting. (Here is one discussion of the subject.)
Whether or not you like how I've done it, I've created a test environment, finally written some tests for those helpers, and fixed some bugs along the way.