Replacing AutoFixture's Default Algorithms by Mark Seemann
Several times in my previous AutoFixture posts, I've insinuated that you can change the algorithms used for creating strings, numbers and so on, if you don't like the defaults.
One way you can do this is by simply using the Register method that I introduced in my previous post. Let's say that you want to replace the string algorithm to simply return a specific string:
fixture.Register<string>(() => "ploeh");
No matter how many times you'll call CreateAnonymous<string> on that particular fixture object, it will always return ploeh.
The Register method is really only a type-safe convenience method that wraps access to the TypeMappings property. TypeMappings is just a Dictionary of types mapped to functions. By default, the Fixture class has a set of pre-defined TypeMappings for primitive types such as strings, numbers and booleans, so you could access the function used to generate strings by indexing into this Dictionary with the System.String type.
Equivalent to the above example, you could alternatively replace the string algorithm like this:
fixture.TypeMappings[typeof(string)] = s => "fnaah";
Instead of using the Register method, I here assign a lambda expression directly to the key identified by the System.String type. This is what the Register method does, so the result is exactly the same.
However, you may have noticed that by accessing TypeMappings directly, the signature of the function is different. The Register method takes a Func<T>, whereas the TypeMappings Dictionary expects a Func<object, object>. As you can see, the Register method is more type-safe, but the TypeMappings Dictionary gives you a chance to utilize the optional seed that one of the CreateAnonymous overloads takes.
You could, for example, do this:
fixture.TypeMappings[typeof(string)] = s => string.Format((string)s, new Random().Next(100));
Although this particular algorithm has a built-in weakness (can you spot it?), we can now use the seed to provide a format string, like this:
string result = fixture.CreateAnonymous("Risk: {0}%");
which will yield a result like Risk: 32%.
When I designed the extensibility mechanism for AutoFixture, I seriously considered defining an interface that all TypeMappings had to implement, but I ended up preferring a Func<object, object> instead, since this allows you to redefine a particular algorithm inline in a test by using an anonymous delegate or lambda expression, and you can also reuse an existing algorithm, as long as it fits the signature.
Comments