In unit or integration tests we often need to create input with data. For example if you test a mapper, all properties of the input must have a value. It will save time if most of the properties are filled in with values automatically.
This can also be used on other scenario's.
[Fact]
public void OrderSender_ShouldSendToCorrectPerson()
{
var genericRandom = new GenericRandom(0);
// Create a Person with random property values.
var person = genericRandom.Next<Person>();
var target = new OrderSender();
var sendResult = target.SendTo(person);
sendResult.Name.Should().Be(person.Name);
}
Just like the .NET Random class, you get the same values if you start with the same seed. That helps to prevent flaky tests (= fails sometimes but not always).
You can use a different seed to get different results, but always the same different results.
Imagine, you have a POCO like this:
public class Person
{
public string Name { get; set; }
public int SingleNumber { get; set; }
public Person Child { get; set; }
public DateTime SomeDate { get; set; }
public List<long> MultipleNumbers { get; set; }
}
You will always get the same values as proofed in these tests:
[Fact]
public void ClassTest_ForPerson_ShouldHaveValuesAtHighestLevel()
{
var target = new GenericRandom(0);
var actual = target.Next<Person>();
actual.Should().NotBeNull();
actual.Name.Should().Be("txvhMh4a");
actual.SingleNumber.Should().Be(97979476);
actual.SomeDate.Should().Be(new DateTime(2000,4,22, 16, 7, 16));
actual.MultipleNumbers.Should().Equal(new List<long>{ 81528902 , 40344430, 51148790, 23214253 });
}
[Fact]
public void ClassTest_ForPerson_ShouldHave3Levels()
{
var target = new GenericRandom(0);
var actual = target.Next<Person>();
actual.Should().NotBeNull();
actual.Name.Should().Be("txvhMh4a");
actual.Child.Should().NotBeNull();
actual.Child.Name.Should().Be("RScmc9B2");
actual.Child.Child.Should().NotBeNull();
actual.Child.Child.Name.Should().Be("pTxz9Brf");
// Maximum 3 levels deep to prevent a stack overflow
actual.Child.Child.Child.Should().BeNull();
}
[Fact]
public void ClassTest_ForString_ShouldHaveSemiRandomResults()
{
var target = new GenericRandom(0);
var actual1 = target.Next<string>();
actual1.Should().Be("txvhMh4a");
// Second call for the same type gives a different result
var actual2 = target.Next<string>();
actual2.Should().Be("RScmc9B");
}
The code of the GenericRandom class can be found on Github. If you use .Net 6, you can also use the Q11.Tools Nuget package..