mardi 9 février 2021

Unit testing theories on collections with xUnit

I was playing around with xUnit so I decided to create a simple class to get the basics right. I decided to go for this:

public class Rollercoaster
{
    public Rollercoaster(int requiredHeight, int minimumAge /* , more parameters */)
    {
        RequiredHeight = requiredHeight;
        MinimumAge = minimumAge;
    }
    public int RequiredHeight { get; set; } // Height in centimeters
    public int MinimumAge { get; set; }
    /* more properties */

    public bool VisitorAllowedToBoard(int height, DateTime dateOfBirth)
    {
        var visitorAge = GetVisitorAge(dateOfBirth);
        return height >= RequiredHeight && visitorAge >= MinimumAge;
    }
    public int GetVisitorAge(DateTime dateOfBirth)
    {
        int age = 0;
        // logic to calculate age
        return age;
    }
}

After creating the Rollercoaster class with some arbitrary requirements for visitors, I created a test library with the following helper methods and a TestVisitor helper class.

public class RollercoasterTests
{
    // Rollercoaster parameters
    public const int CoasterRequiredHeight = 150;
    public const int CoasterMinimumAge = 12;

    // Visitor restrictions
    public const int VisitorMinAge = 0;
    public const int VisitorMaxAge = 1000; // Arbitrary upper bound, in years
    public const int VisitorMinHeight = 0;
    public const int VisitorMaxHeight = 300; // Arbitrary upper bound, in centimeters

    public RollercoasterTests() {}
    
    #region Helper Stuff
    public Rollercoaster Rollercoaster { get; set; } = new Rollercoaster() { RequiredHeight = CoasterRequiredHeight, CoasterMinimumAge = 12 };
    public List<TestVisitor> AllowedVisitors { get; set; } = GetRandomVisitors(CoasterMinimumAge, VisitorMaxAge, CoasterRequiredHeight, VisitorMaxHeight);
    public List<TestVisitor> DisallowedVisitors { get; set; } = GetRandomVisitors(VisitorMinAge, VisitorMaxAge, VisitorMinHeight, CoasterRequiredHeight);

    public List<TestVisitor> GetRandomVisitors(int minAge, int maxAge, int minHeight, int maxHeight, int count = 1_000_000)
    {
        var visitors = new List<TestVisitor>();
        var rng = new Random();
        for (int i = 0; i < count; i++)
        {
            var randomHeight = rng.Next(minHeight, maxHeight);
            var randomAge = rng.Next(minAge, maxAge);
            var randomDateOfBirth = GenerateValidDateOfBirth(randomAge);
            var visitor = new Visitor(randomHeight, randomDateOfBirth);
            visitors.Add(visitor);
        }
        return visitors;
    }

    public DateTime GenerateValidDateOfBirth(int age = 18)
    {
        DateTime dateOfBirth = default;
        // Logic to generate valid date of birth
        return dateOfBirth;
    }
    #endregion Helper Stuff

    [Theory]
    [/* what goes here? */] // I want to test for every visitor in AllowedVisitors
    public void TallEnoughAndOldEnough(/* what goes here? */)
    {
        var visitorHeight = /* get height of each visitor */;
        var visitorDateOfBirth = /* get date of birth of each visitor */;
        Assert.True(Rollercoaster.VisitorAllowedToBoard(visitorHeight, visitorDateOfBirth));
    }

    [Theory]
    [/* what goes here? */] // I want to test for every visitor in DisallowedVisitors
    public void NotTallEnoughOrOldEnough(/* what goes here? */)
    {
        var visitorHeight = /* get height of each visitor */;
        var visitorDateOfBirth = /* get date of birth of each visitor */;
        Assert.False(Rollercoaster.VisitorAllowedToBoard(visitorHeight, visitorDateOfBirth));
    }
}
public class TestVisitor
{
    public TestVisitor() {}
    public TestVisitor(int height, DateTime dateOfBirth)
    {
        Height = height;
        DateOfBirth = dateOfBirth;
    }
    public int Height { get; set; }
    public DateTime DateOfBirth { get; set; }
}

How could I test the requirements for an arbitrary sample of randomly generated visitors in xUnit? I don't want to create a million InlineData entries manually, and I can't quite wrap my head around the MemberData attribute.

Aucun commentaire:

Enregistrer un commentaire