NonMockObjects 0.2.0 Released
I've released NonMockObjects 0.2.0.
This release adds easy support for obtaining many variants of an object, which allows you to easily test that all combinations of some parameters have a certain invariant.
For instance, my Entry objects for this weblog can optionally contain a title, a link for the title, and a summary. The only constraint on that set is that a link affects a title, so you really need a title for the link to make sense, although it is permissible to have a link without a title in the database. By setting up my Entry creation function to indicate that each of those can either be None or contain a certain concrete string, I can obtain all 8 combinations of an entry with:
for entry in data.variations_entry():
And then perform some tests, such as verifying that no code crashes and that the RSS renders all combinations correctly.
You'd be surprised what such combinatoric testing can reveal. I think such combinatoric testing is a powerful argument in favor of automated testing; you already can't possibly run manual tests as often as you run automated tests, but testing umpteen combinations of parameters really highlights the difference. Manually testing what can rapidly become hundreds of combinations of parameters can be impossible, even once at the end of a project, and you certainly aren't going to do it repeatedly, let alone every time the code changes.
Obviously you have to be careful not to go overboard and accidentally specify a few million combinations, but so far I have not found that to be a problem in practice. If you have an object that truly has a million distinct behaviors, you've probably got a design problem.
The unit testing on this weblog's code still isn't quite up to par (I just now noticed I have no category index pages, whoops!), but it'd be even worse without the support of NonMockObjects.
If there's going to be a 0.2.1, it'll probably add the ability to request the exact set of parameters that was used to create the object along with the object, that is, code that looks like the following:
for object, creation_parameters in data.variations_entry():
where creation_parameters is a dict containing the concrete parameters used to create the object this time through the loop.
I'm still trying to work out whether this constitutes an antipattern or not. The argument against it is that you really ought to be able to easily re-discover the parameters by examining the returned object, and if you can't, it might be a sign that your creation functions are too complicated. The argument in favor of it is "complexity happens", and might still be easier to work with the parameters directly. So far I've always been able to examine the returned object.
For full documentation on the process of creating and using variations, see the Variations header in the API documentation.