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.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s