Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to fix a range on some properties when create a TestClass by AutoFixture

How can I tell AutoFixture to specify a range (min and max) on some properties when doing

MyDataClass obj = fixture.Create<MyDataClass>();

where MyDataClass has property Diameter and I only want min:1 and max:60 on this property?

like image 912
sowen Avatar asked Mar 27 '14 15:03

sowen


2 Answers

You could simply add a specific ICustomization<MyDataClass> when instantiating the fixture:

IFixture fixture = new Fixture();
fixture.Customize<MyDataClass>(c => c
  .With(x => x.Diameter, () => new Random().Next(1, 61)); // maxValue is excluded, thus 61

Now, whenever you use fixture.Create<MyDataClass>(), a new random value between 1 and 60 is set on the created instance.

like image 163
J. Molenkamp Avatar answered Sep 29 '22 12:09

J. Molenkamp


Data Annotations

The easiest approach is probably adorning the property itself with a Data Annotation, although I'm not myself a huge fan of this:

public class MyDataClass
{
    [Range(1, 60)]
    public decimal Diameter { get; set; }
}

AutoFixture will respect the [Range] attribute's values.

Convention-based

A better approach is, in my opinion, a convention-based approach that doesn't rely on non-enforceable attributes:

public class DiameterBuilder : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as PropertyInfo;
        if (pi == null ||
            pi.Name != "Diameter" ||
            pi.PropertyType != typeof(decimal))
            return new NoSpecimen(request);

        return context.Resolve(
            new RangedNumberRequest(typeof(decimal), 1.0m, 60.0m));
    }
}

This passing test demonstrates how to use it:

[Fact]
public void ResolveRangeLimitedType()
{
    var fixture = new Fixture();
    fixture.Customizations.Add(new DiameterBuilder());
    var actual = fixture.Create<Generator<MyDataClass>>().Take(100);
    Assert.True(actual.All(x => 1 <= x.Diameter && x.Diameter <= 60));
}

For more details, please refer to this other, very closely related SO Q&A.

Overcoming Primitive Obsession

Perhaps an even better approach is to listen to your tests, combat Primitive Obsession, and introduce a custom type - in this case, a Diameter Value Object.

This is often my preferred approach.

like image 37
Mark Seemann Avatar answered Sep 29 '22 11:09

Mark Seemann