Reputation: 531
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!
Upvotes: 4
Views: 1481
Reputation: 233397
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);
}
Upvotes: 4
Reputation: 11211
There should be a couple of ways of customizing AutoFixture for this. – Here are a few of them:
With Data Annotations
public class Point
{
public double X { get; private set; }
public double Y { get; private set; }
public Point(
[Range( -90, 90)]double x,
[Range(-180, 180)]double y)
{
if (x > 90 || x < -90)
throw new ArgumentOutOfRangeException("latitude");
if (y > 180 || y < -180)
throw new ArgumentOutOfRangeException("longitude");
this.X = x;
this.Y = y;
}
}
[Fact]
public void CreatePointDoesNotThrow()
{
var fixture = new Fixture();
Assert.DoesNotThrow(() => fixture.Create<Point>()); // Passes.
}
With Generators
public class Point
{
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");
this.X = x;
this.Y = y;
}
}
[Fact]
public void CreatePointDoesNotThrow()
{
var fixture = new Fixture();
var x = new Generator<int>(fixture).First(pt => pt > -90 && pt < 90);
var y = new Generator<int>(fixture).First(pt => pt > -180 && pt < 180);
fixture.Customize<Point>(c => c
.FromFactory(() => new Point(x, y)));
Assert.DoesNotThrow(() => fixture.Create<Point>()); // Passes.
}
Upvotes: 4