AutoFixture Default Builders

AutoFixture takes the heavy lifting out of creating test fixtures. If you haven’t used it before and want an intro, then check out the wiki.

I’ve experienced it’s use on a number of projects, and would generally recommend it.

However, some care must be taken as it generates some of the types in a constrained non-deterministic way. So if applied to non-anonymous variables that affect the outcome of the test then the test may fail intermittently.

AutoFixture comes with a set of default builders encapsulating the rules for creating a variety of .NET CTS types. It also provides points of extensibility that allow you to customize the rules.

The library has been developed over a number of years, and the documentation on the rules is quite fragmented. So I thought I’d dig into the source code and document what I find, as much for my reference and benefit as yours.

Note. The following applies to the current version (v3.0) of AutoFixture.

Numbers

var fixture = new Ploeh.AutoFixture.Fixture();

Console.WriteLine("{0}, {1}, {2}",
                  fixture.Create<int>(),
                  fixture.Create<double>(),
                  fixture.Create<byte>());
79, 236, 44

The default builder for numbers is RandomNumericSequenceGenerator. It generates a sequence of unique numbers for the following types – Byte, Decimal, Double, Int16, Int32, Int64, SByte, Single, UInt16, UInt32, UInt64.

Unique numbers are generated randomly from the set [1, 255]. Once these are used up they are then be generated from the set [256, 65,535]. And finally from the set [65,536,  2,147,483,647]. When all numbers within the final set have been used AutoFixture will start again from the first set.

fixture.Customizations.Add(
    new RandomNumericSequenceGenerator(1, 10)
);

Console.WriteLine("{0}, {1}, {2}",
                  fixture.Create<int>(),
                  fixture.Create<int>(),
                  fixture.Create<int>());
10, 6, 8

RandomNumericSequenceGenerator has a constructor that takes a variable number of Int64 limits used to constrain the sets. There must be at least 2 limits, hence one set, and they must be specified in numeric order.

By adding an instance of RandomNumericSequenceGenerator to the Fixture.Customizations collection, you can constrain the generated numbers by specifying custom limits; as shown above where the numbers are constrained between 1 and 10.

fixture.Customizations.Add(
    new NumericSequenceGenerator()
);

Console.WriteLine("{0}, {1}, {2}",
                  fixture.Create<int>(),
                  fixture.Create<int>(),
                  fixture.Create<int>());
1, 2, 3

The RandomNumericSequenceGenerator was introduced with v3.0. Earlier versions used the NumericSequenceGenerator which generates numbers in sequence starting at 1. If you wish to have AutoFixture generate numbers in a deterministic manner then you can specify the usage of NumericSequenceGenerator by adding it to the Customizations collection.

Chars

Console.WriteLine("{0}, {1}, {2}",
                  fixture.Create<char>(),
                  fixture.Create<char>(),
                  fixture.Create<char>());
Y, /, A

The default builder for char is RandomCharSequenceGenerator, which generates random characters from the printable ASCII character set (“!” (33) to “~” (126)).

fixture.Customizations.Add(
    new CharSequenceGenerator()
);

Console.WriteLine("{0}, {1}, {2}",
                  fixture.Create<char>(),
                  fixture.Create<char>(),
                  fixture.Create<char>());
!, ", #

For a more deterministic approach the CharSequenceGenerator builder can be used to cycle through the printable ASCII character set.

Strings

Console.WriteLine("{0}", fixture.Create<string>());
 eee9e1b5-70a1-4b61-952d-c6fa4c14b166

StringGenerator is a builder that has a constructor accepting a delegate (Func<object>), which is invoked in order to generate strings.

StringGenerator is registered as the default builder for strings an initialized with the following function – () => Guid.NewGuid(). It therefore generates unique Guids for strings.

Console.WriteLine("{0}", fixture.Create<string>().Substring(0, 10));
22ca485a-7

A Guid, including the hythens, is 36 characters in length. If you require a string with less characters, and do not want to go to the lengths of creating and registering a custom string builder, then the easiest thing to do is apply the Substring method to the resultant string.

Console.WriteLine("{0}", string.Join(string.Empty, fixture.CreateMany<string>(5)));
edd22091-e048-4d22-a41c-0c50482f65d96d741962-9b9b-4431-9d17-6993986933cd1a3814e5-3989-4448-8ea2-5bc8de4d55744314904f-8884-4af4-8253-45c7f1dc11306b0e2212-bae9-44de-8754-5dd62ebcf2a0

Generating a longer string is a little more complex, but can be achieved by using the Fixture.CreateMany() method to generate a collection of strings, and then join these together using String.Join.

using System.ComponentModel.DataAnnotations;
public class Product
{
    public int ProductId { get; set; }
   
    [StringLength(10)]
    public string Name { get; set; }
}

Console.WriteLine("{0}", fixture.Create<Product>().Name);
60ebc6bc-5

ConstrainedStringGenerator is registered as a default handler for strings, in addition to StringGenerator. The difference is that ConstrainedStringGenerator is used to constrain strings when they are annotated with the StringLength  attribute. It generates strings by concatenating Guids, and then taking a Substring of the Guids to ensure an exact length.

fixture.Register<string>(() => "Lorem ipsum dolor sit amet, consectetur adipiscing elit.");

Console.WriteLine("{0}", fixture.Create<string>());Console.WriteLine("{0}", fixture.Create<string>());
Lorem ipsum dolor sit amet, consectetur adipiscing elit.

AutoFixture has a Register method, which allows the function used to generate instances of a specific type to be overridden. In the above example, the Register method overrides the default method used for generating strings to one that generates Lorem Ipsum text. not sure how useful this is; it’s just an example.

One small caveat with overriding the string function as shown above. If you the try to generate a Uri, you will receive the following error ” Invalid URI: The hostname could not be parsed.”. the Uri utilizes the string function to generate the hostname, and hostnames can only comprise of a limited number of characters, which does not include spaces or commas.

DateTime

Console.WriteLine("{0}, {1}, {2}",
                  fixture.Create<DateTime>(),
                  fixture.Create<DateTime>(),
                  fixture.Create<DateTime>());
04/11/2016 00:59:14, 27/11/2017 10:33:06, 26/12/2015 04:33:45

RandomDateTimeSequenceGenerator is the default builder for DateTime objects. It has a constructor that takes a minimum and maximum DateTime, and it randomly generates a date between these limits. by default the limits are set to DateTime.Now.AddYears(-2) and DateTime.Now.AddYears(2).

fixture.Customizations.Add(
    new RandomDateTimeSequenceGenerator(new DateTime(2017, 1, 1), new DateTime(2017, 1, 31))
);

Console.WriteLine("{0}, {1}, {2}",
        fixture.Create<DateTime>(),
        fixture.Create<DateTime>(),
        fixture.Create<DateTime>());
06/01/2017 07:11:33, 26/01/2017 19:17:35, 01/01/2017 16:18:43

By adding the RandomDateTimeSequenceGenerator to the Customizations collection and specifying minimum and maximum DateTime limits you can customize the range from which DateTime values are generated. In the example above values are generated for the year 2017.

AutoFixture has 2 addition builders for DateTime: StrictlyMonotonicallyIncreasingDateTimeGenerator, which takes a seed DateTime as a constructor parameter, and increments each subsequent generated DateTime by one day; and CurrentDateTimeGenerator which returns DateTime.Now.

Uri

Console.WriteLine("{0}", fixture.Create<Uri>());

fixture.Inject(new UriScheme("ftp"));
Console.WriteLine("{0}", fixture.Create<Uri>());
http://0d171f49-5624-417f-a826-2866b166e225/
ftp://cf56a59a-5390-4c78-807a-7a4cd01b6eec/

UriGenerator is used to build instances of Uri, defaulting the scheme to “http” and the authority generated by StringGenerator, hence a Guid.

The scheme can be customized by injecting an instance of UriScheme with the scheme passed to the constructor, as shown above for “ftp”.

The authority can be customized by registering a new string builder function. But as mentioned in the Strings section, the string generator function should only generate strings containing only characters compatible with URIs.

Regular Expressions

public class Contact
{
    [RegularExpression(@"^[2-9]\d{2}-\d{3}-\d{4}$")]
    public string Telephone { get; set; }
}

Console.WriteLine("{0}", fixture.Create<Contact>().Telephone);
601-000-1101

As mentioned in the Strings section, the ConstrainedStringGenerator builds strings of specific lengths where AutoFixture is used to generate a custom type that has a string property annotated with the StringLength attribute.

Similarly, the RegularExpressionGenerator builds strings annotated with a RegularExpression attribute.

Booleans

Console.WriteLine("{0}, {1}",
                  fixture.Create<bool>(),
                  fixture.Create<bool>());
True, False

BooleanSwitch is the default builder for Booleans, generating in a deterministic manner, alternating between True and False.

Guids

Console.WriteLine("{0}", fixture.Create<Guid>());
2fac899e-55a2-4161-a90f-ec7e7b87ca92

GuidGenerator returns Guid.NewGuid().

Delegates

public bool void Process(int orderId);

Console.WriteLine("{0}, {1}", func.Method, func.Invoke(1));
Boolean lambda_method(System.Runtime.CompilerServices.Closure, Int32), True

DelegateGenerator is the default builder for delegates. It returns a delegate pointing to a dynamically generated method.

If the delegate has a return value, then the method will return a generated instance of that value when invoked.

MailAddress

Console.WriteLine("{0}", fixture.Create<MailAddress>());
"43b37eb4-48ee-49e2-bc8c-1bed70fc1796" <43b37eb4-48ee-49e2-bc8c-1bed70fc1796@example.com>

The MailAddressGenerator builder generates instances of MailAddress.

The user part of the address is generated using the registered string generator, which by default returns Guid.NewGuid(). As mentioned in the Url section, be careful when registering a custom string generator function, as it must only return compatible characters, so no spaces in commas for example.

The host part of the address is generated using the DomainNameGenerator builder, which randomly returns one of the following domains – example.com, example.net, example.org.

fixture.Register(() => new DomainName("acmecorp.com"));

Console.WriteLine("{0}", fixture.Create());

"393afa04-be57-4275-afbf-7349a1ba54d3" <393afa04-be57-4275-afbf-7349a1ba54d3@acmecorp.com>

If you want to use a custom host, then you can register a custom function for generating DomainName instances, as shown above.

Self testing code != TDD != Unit testing

Test Driven Development (TDD) has been a buzz-word in the industry for many years now, and with the increasing popularity of frameworks that support isolation of components – and I’m thinking MVC here – TDD is a skill-set that is very much in demand. But is this simply because TDD is being equated with self-testing code, and developers are not standing back and questioning whether self-testing code could be produced in a more productive and engaging way?

Of course I would not consider a work item to be complete unless it has, or has been considered for, a good coverage of workable tests. But I feel a little uneasy with adopting a “always test-first” approach to development. Sometimes it may be beneficial to break away from this approach in order to go with the flow of development. Also, I feel a little uneasy with the prevalent view that you should be able to fully test all units of code in isolation from dependencies, through the use of interfaces and mocking. Testing can be simplified in some circumstances by including dependencies. The number of tests can be reduced, and more effort can be spent on writing good well-factored code rather than on mocking complex objects. I also feel uncomfortable with a drive for 100% test coverage. Does this become an impediment to change?

Don’t get me wrong, TDD is a good approach to development. Just not in all situations. And it’s not the only approach.

The reason I am writing this post is that I’ve just watched a series of Google hangout videos on YouTube – called “Is TDD dead?” – where Martin Fowler, Kent Beck, and David Hannson debate these issues. They are well worth a look –

My take aways from the discussions are as follows –

1) TDD is an approach which produces self-testing code. But self-testing code can also be produced without following the TDD approach. It is not necessary to test-first. Test-after can produce the same test coverage. Self-testing code != TDD.

2) There is a drive in the development community to fully isolate all layers and units of code so that they can be tested independently from other layers and units. Code is therefore designed with unit-testing in mind, and as such may not be optimally designed for comprehension. This drive is a consequence of the uptake in the TDD approach. But TDD equates to red-green-refactor. It does not require the full isolation of units of code, and in many cases the approach can be followed without doing so, as long as the tests are designed to be repeatable. TDD != Unit Testing. But loose coupling of code is not necessarily a bad thing. There are other benefits besides testing. Such as the ability to switch dependencies such as data providers and services.

3) A rigid approach to TDD may lead to over-testing. What is good test coverage? Enough so that you can confidently change the code with the expectation that any errors you introduce will be highlighted by a failed test. But not so much that more effort is spent changing and maintaining tests then on changing code.

4) the TDD approach or discipline is a good one to have, but should not necessarily be followed in all situations. Whether it is used or not, the goal should be to implement a good set of regression tests.