Reduce unit test friction patterns - 5 - Autofixture as an automatic Test Data Builder
This is largely the same problem as the Test Data Builder, you have non trivial test data that needs repeated construction in many tests. I’m going to modify the example here though so it fits the use case of autofixture a little better, so we now have Lion and Rabbit Animal classes that we instantiate rather than just using a type on the Animal object:
So we have a zoo, which houses animals, each animal is its own type, but each one also has a Name field which must be passed to the constructor which we don’t care about in this particular test. Each one implements a Feed and IsFed method which I’ll leave to the readers imagination as to how they work.
Now.. the test data builder solution is to create a builder to construct your test objects, so your test now looks like :
We choose not to bother setting the name in the builder, and just have it create 3 of each type of animal, then the rest of the test continues as the previous test did. In this case, the builder requires less code in the test, and is more readable, but will take a lot more code to create the builder itself and unless you have a complex object model, the trade off isn’t always worth it.
The Autofixture Solution
This is where a test object creation tool like Autofixture comes in extremely handy. Basically it is a generic test data builder, so you can tell it to create many objects of a given type, give it the amount you want it to create, and it will use internal (customisable) algorithms to create that number pseudo random objects, so for strings an example would be:
This will create an enumerable of 20 strings, each populated according to its current configuration. It will also work with more complicated objects too:
AutoFixture will automatically generate 5 Person objects in this code. The names will be pseudo random sequences of characters, and the birth dates won’t make sense, but this can still be useful data in a test scenario.
Using Autofixture allowed us to reduce the amount of setup code required for the string nad Person examples above, lets see how this looks with the Zoo example:
This will generate 3 lions and 3 rabbits, all with pseudo random names, the random names will automatically get piped into each objects constructor, and we don’t need to worry about setting the name field that we’re not interested in for this test. There is slightly more code in the test than with a dedicated builder object, but we do away with maintaining a separate builder object completely, and we’ve reduced the original setup code by several lines. This makes autofixture incredibly powerful as a simple test object builder, and in many cases you can create your model objects without having to worry about the parameters you are not explicitly testing for (just like our name field in the Animal object in the above test).
Where it applies
Autofixture as a data builder is pretty useful for objects in tests where the value of the objects doesn’t have to be specific, it just needs to be the correct type, or in cases where the test data objects have a bunch of constructor parameters that you don’t really care about (i.e. the name in the above example). Autofixture also makes a good internal addition to a TestDataBuilder, for when you want to have more automatic ways to generate certain objects, but still want to keep a more readable interface for the test itself.
Where it probably shouldn’t apply
Autofixture is not as good in places where you need to have very specific values for test data objects. I generally find if I am trying to heavily customise the standard AutoFixture algorithms (i.e. start defining weird sequences of numbers, or a bunch of valid strings that can be set) the tests can actually become more complicated than they would do by just writing the test data construction code manually. Customizations can be standardised though, so in some cases, its well worth creating a customisation for a commonly used type and reusing that in many tests.
AutoFixture functions as an incredibly powerful automatic test data builder, but its automatability is both a blessing and a curse, if used sparingly in simpler scenarios it can really improve your test code by reducing the amount of filler code you have to write, if used in more complex scenarios it can be more complicated than manually creating the data, and somewhat obfuscate the readability of the tests.
AutoFixture is well worth the effort of learning and having in your toolkit, when it does apply, it really is a time saver, just remember that it doesn’t apply everywhere.