The Zero-One-Infinity rule is a computer science adage from Willem van der Poel meant to compel software designers to avoid arbitrary limits. In thinking about this recently, I’ve come to appreciate zero-one-infinity as a useful heuristic for certain categories of tests. Let me illustrate this with a few examples, starting from the high numbers and working my way downward.
The number of unit tests for a single component will tend to be as large as the associated feature set and allowed input combinations, with no predetermined upper bound. Of course, in practice there are two pressures that will stop you before true infinity — the single responsibility principle and the need for finite computability. For the former, you want your objects to be reasonably focused in what they provide. For the latter, you need to pick a reasonable limit for how many tests you can write, run, and maintain; e.g. a BigInteger implementation cannot be exhaustively tested, but has various significant partitions of inputs that could expose code bugs.
The limit of one typically occurs at the intersection between two components or at an upper layer. If A uses B and B already has tests of its own, it would be inefficient and redundant to attempt to get coverage of B through the lens of A. Instead, you probably want one validation that A + B functions as expected and a whole lot more focus on what A uniquely brings to the table without worrying about B specifically.
There are plenty of things that are not worth testing at all. In some cases, you end up with little more than a tautology, such as a test that checks if a DTO property when set to “my value” ends up with value “my value.” If the test is the implementation and vice versa, you should just delete the test. Similarly, there will always be some code in your system that uses test-unfriendly features, since sooner or later we all have to deal with the outside world. The trick is pulling all of the “interesting” parts of your code closer to you (a technique @jbrains discusses in his excellent “Integrated tests are a scam!” talks), and pushing outward the simplest possible “boring” code to deal with those pesky externals. If you succeed in this, you can pretty safely verify by inspection (and probably stepping through it once) that the “boring” code is right, leaving the need for a zero actual tests.