Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use the RandomRangedNumberCustomization in Autofixture to ensure parameters are in a certain range?

I have a class that takes two parameters as shown:

    public double X { get; private set; }

    public double Y { get; private set; }

    public Point(double x, double y)
    {
        if (x > 90 || x < -90)
            throw new ArgumentOutOfRangeException("latitude");

        if (y > 180 || y < -180)
            throw new ArgumentOutOfRangeException("longitude");

        X = x;
        Y = y;
    }

The corresponding properties are set in the constructor, so I need to tell AutoFixture to create a Point class with parameters in the range as specified in the guard clauses. I have managed to get a bit confused about the usage of the RandomRangedNumberCustomization class. I did the following:

        var xRange = new RangedNumberRequest(typeof(double), -90.0, 90.0);
        var yRange = new RangedNumberRequest(typeof (double), -180.0, 180.0);
        var dummyContext = new DelegatingSpecimenContext();
        var generator = new RandomRangedNumberGenerator();
        var x = (double)generator.Create(latitudeRange, dummyContext);
        var y = (double) generator.Create(longitudeRange, dummyContext);

which will generate numbers in my range, so I could just create a Point and feed in those generated numbers, but I'm missing something in terms of the customization. Any help and or guidance would be much appreciated.

Thanks!

like image 630
TortillaCurtain Avatar asked Feb 11 '23 02:02

TortillaCurtain


1 Answers

Apart from the solutions suggested by Nikos Baxevanis, here's another option. You can implement a convention that always handles the x and y arguments for Point in a particular way:

public class PointCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(new XBuilder());
        fixture.Customizations.Add(new YBuilder());
    }

    private class XBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var pi = request as ParameterInfo;
            if (pi == null ||
                pi.Name != "x" ||
                pi.Member.DeclaringType != typeof(Point))
                return new NoSpecimen(request);

            return context.Resolve(
                new RangedNumberRequest(typeof(double), -90d, 90d));
        }
    }

    private class YBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var pi = request as ParameterInfo;
            if (pi == null ||
                pi.Name != "y" ||
                pi.Member.DeclaringType != typeof(Point))
                return new NoSpecimen(request);

            return context.Resolve(
                new RangedNumberRequest(typeof(double), -180d, 180d));
        }
    }
}

(There's room for refactoring in the above code.)

Given PointCustomization, this test passes:

[Fact]
public void CreatePointDoesNotThrow()
{
    var fixture = new Fixture().Customize(new PointCustomization());
    var e = Record.Exception(() => fixture.Create<Point>());
    Assert.Null(e);
}
like image 59
Mark Seemann Avatar answered Feb 13 '23 03:02

Mark Seemann