Creating Repeatable Test Data with the Bogus NuGet Library

November 05, 2022

One of the things that has always bothered me about using libraries like AutoFixture and Bogus is that the data they produce is random, and I very much dislike having random data in tests… because it’s random, and so makes the test non-deterministic.

I’ve heard a few contrary arguments to this: mainly that having a test that fails some of the time indicates that there’s an issue that a completely deterministic test may miss. My counter argument is that I’ve worked in enough places where non-deterministic tests of every kind have led to a test suite that intermittently fails, which has a Skinner Box effect on the team: basically, they know that if the tests failed locally, they re-run them and they’ll succeed; if it fails in the build, just re-run the build. This is, in fact, the best outcome I’ve seen of such test suites - the worst being that the team simply don’t trust the tests, and so just stop running them altogether.

But you can make libraries like Bogus deterministic!

What?

This was both surprising and obvious to me when I discovered this (BTW, I discovered this recently, and I think someone told me, but I can’t remember for the life of me who - so apologies if it was you). Let’s take some code from this library:

using Bogus;

var fakerTest = new Faker<TestClass>()
    .RuleFor(a => a.Name, b => b.Company.CompanyName())
    .RuleFor(a => a.Url, b => b.Internet.Url())
    .RuleFor(a => a.Description, b => b.Lorem.Paragraph())
    .RuleFor(a => a.Quantity, b => b.Random.Number(1, 5))
    .RuleFor(a => a.Address, b => b.Address.FullAddress())
    .RuleFor(a => a.Amount, b => b.Random.Decimal(2, 10))
    .RuleFor(a => a.OrderDate, b => b.Date.Past(1));

var testClass = (TestClass)fakerTest;
Console.WriteLine(testClass.ToString());

If you run this code twice, it’ll give different results. Also, if you’re interested in the class, it’s here:

internal class TestClass
{
    public string Url { get; set; }
    public DateTime OrderDate { get; set; }
    public string Description { get; set; }
    public int Quantity { get; set; }
    public decimal Amount { get; set; }
    public string Name { get; set; }
    public string Address { get; set; }

    public override string ToString()
    {
        return $"Url: {Url}\nOrder Date: {OrderDate}" +
            $"\nDescription: {Description}\nQty: {Quantity}" +
            $"\nAmount: {Amount}\nName: {Name}\nAddress: {Address}";
    }
}

There’s no such thing as random

This is the bit that was obvious (although it hadn’t ocurred to me), there are no random numbers in computing. Any random number that matters (think gambling) is generated by use of a hardware RNG, which use background radiation and other factors that I don’t understand to generate the number.

A software random number, whilst difficult to predict, is definitely not random. It just takes a given seed and then does some complex maths - give it the same seed and you’ll get the same result.

Turns out that Bogus exposes that seed for you; for example:

var fakerTest = new Faker<TestClass>()
    .RuleFor(a => a.Name, b => b.Company.CompanyName())
    .RuleFor(a => a.Url, b => b.Internet.Url())
    .RuleFor(a => a.Description, b => b.Lorem.Paragraph())
    .RuleFor(a => a.Quantity, b => b.Random.Number(1, 5))
    .RuleFor(a => a.Address, b => b.Address.FullAddress())
    .RuleFor(a => a.Amount, b => b.Random.Decimal(2, 10))
    .RuleFor(a => a.OrderDate, b => b.Date.Past(1))
    .UseSeed(50);


var testClass = (TestClass)fakerTest;
var testClass2 = (TestClass)fakerTest;

Console.WriteLine(testClass.ToString());
Console.WriteLine(testClass2.ToString());

This now returns the same result twice. That is, if you run it twice - the second class will contain different data from the first, because the RNG is designed to rotate the seed based on the result; you’ll see behaviour that proves this if you repeatedly instantiate the .Net RNG in a tight loop, as it uses a seed that’s based on the time of day.



Profile picture

A blog about one man's journey through code… and some pictures of the Peak District
Twitter

© Paul Michaels 2022