Question

We have MVC4 project with Entity Framework for storage. For our tests we recently started using Autofixture and it is really awesome.

Our models graph is very deep and usually creating one object by AutoFixture creates the whole graph: Person -> Team -> Departments -> Company -> Contracts -> .... etc.

The problem with this is time. Object creation takes up to one second. And this leads to slow tests.

What I find myself doing a lot is things like this:

        var contract = fixture.Build<PersonContract>()
            .Without(c => c.Person)
            .Without(c => c.PersonContractTemplate)
            .Without(c => c.Occupation)
            .Without(c => c.EmploymentCompany)
            .Create<PersonContract>();

And this works and it is quick. But this over-specification makes tests hard to read and sometimes I loose the important details like .With(c => c.PersonId, 42) in the list of unimportant .Without().

All these ignored objects are navigational properties for Entity Framework and all are virtual.

Is there a global way to tell AutoFixture to ignore virtual members?

I have tried creating ISpecimentBuilder, but no luck:

public class IgnoreVirtualMembers : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {

        if (request.GetType().IsVirtual // ?? this does not exist )
        {
             return null;
        }
    }
}

I can't seem to find a way in ISpecimenBuilder to detect that object we are constructing is a virtual member in another class. Probably ISpecimenBuilder this is not the right place to do this. Any other ideas?

Was it helpful?

Solution

Read a bit more on Mark's blog (this particularly) I found the way to do what I want:

/// <summary>
/// Customisation to ignore the virtual members in the class - helps ignoring the navigational 
/// properties and makes it quicker to generate objects when you don't care about these
/// </summary>
public class IgnoreVirtualMembers : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        if (context == null)
        {
            throw new ArgumentNullException("context");
        }

        var pi = request as PropertyInfo;
        if (pi == null)
        {
            return new NoSpecimen(request);
        }

        if (pi.GetGetMethod().IsVirtual)
        {
            return null;
        }
        return new NoSpecimen(request);
    }
}

And you can wrap these into customisation:

public class IgnoreVirtualMembersCustomisation : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customizations.Add(new IgnoreVirtualMembers());
    }
}

So in your test you just do:

var fixture = new Fixture().Customize(new IgnoreVirtualMembersCustomisation());

and go create your complex models.

Enjoy!

OTHER TIPS

I had this same problem and decided to go one step further and create a customization to lazy-load navigation properties. The project is on Github and NuGet.

Consider the simple object graph below which has a circular dependency:

class Foo
{
    public int Id { get; set; }
    public int BarId { get; set; }
    public virtual Bar Bar { get; set; }
}

class Bar
{
    public int Id { get; set; }
    public int FooId { get; set; }
    public virtual Foo Foo { get; set; }
}

With this customization, calling var foo = fixture.Create<Foo>() will create an object of type Foo. Calling the foo.Bar getter will use DynamicProxy and AutoFixture to create an instance of Bar on the fly and assign it to that property. Subsequent calls to foo.Bar return the same object.

N.B. the customization is not smart enough to set foo.Bar.Foo = foo - this must be done manually if needed

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top