davidk
davidk

Reputation: 814

Why can't AutoFixture create this class?

Summary

I am encountering strange behavior using AutoFixture that seems to be related to class inheritance, recursion, or collections. Is there is anything specific I need to do to allow AutoFixture to create these classes? Is there some limitation of AutoFixture I am missing?

Details

Given classes as follows

public class SpecificNodeA : BaseNode
{
    public SpecificNodeA(IEnumerable<BaseNode> childNodes) : base(childNodes)
    {
    }
}

public abstract class BaseNode
{
    public List<BaseNode> ChildNodes { get; }

    protected BaseNode(IEnumerable<BaseNode> childNodes)
    {
        ChildNodes = childNodes?.ToList();
    }
}

public class SpecificNodeB : BaseNode
{
    public SpecificNodeB(IEnumerable<BaseNode> childNodes) : base(childNodes)
    {
    }
}

and a test class of

[TestClass]
public class UnitTest1
{
    [TestMethod]
    public void CanGetSpecificNodeA()
    {
        var fixture = GetFixture();
        var node = fixture.Create<SpecificNodeA>();
    }

    [TestMethod]
    public void CanGetSpecificNodeB()
    {
        var fixture = GetFixture();
        var node = fixture.Create<SpecificNodeB>();
    }

    private Fixture GetFixture()
    {
        var fixture = new Fixture();
        fixture.Customizations.Add(
            new TypeRelay(
                typeof(BaseNode),
                typeof(SpecificNodeA)));
        fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
            .ForEach(b => fixture.Behaviors.Remove(b));
        fixture.Behaviors.Add(new OmitOnRecursionBehavior());
        return fixture;
    }
}

I get the error below when trying to create an instance of SpecificNodeB

 Test method AutoFixtureTest.UnitTest1.CanGetSpecificNodeB threw exception: 
AutoFixture.ObjectCreationExceptionWithPath: AutoFixture was unable to create an instance from AutoFixtureTest.SpecificNodeA, most likely because it has no public constructor, is an abstract or non-public type.

Request path:
    AutoFixtureTest.SpecificNodeB
      System.Collections.Generic.IEnumerable`1[AutoFixtureTest.BaseNode] childNodes
        System.Collections.Generic.IEnumerable`1[AutoFixtureTest.BaseNode]
          AutoFixtureTest.BaseNode
            AutoFixtureTest.SpecificNodeA

If However I change the classes to not have the collection (as below) they work fine

public class SpecificNodeA : BaseNode
{
    public SpecificNodeA(int nodeId) : base(nodeId)
    {
    }
}

public abstract class BaseNode
{
    public int NodeId { get; }

    protected BaseNode(int nodeId)
    {
        NodeId = nodeId;
    }
}

public class SpecificNodeB : BaseNode
{
    public SpecificNodeB(int nodeId) : base(nodeId)
    {
    }
}

It seems odd to me that AutoFixture can not create these classes since they are fairly simple and the error message indicating it

was unable to create an instance from AutoFixtureTest.SpecificNodeA

doesn't make sense since it clearly can as seen in the first unit test.

Is there is anything specific I need to do to allow AutoFixture to create these classes? Is there some limitation or aspect of AutoFixture I am missing or missunderstanding?

Upvotes: 4

Views: 2476

Answers (1)

Jens Hykkelbjerg
Jens Hykkelbjerg

Reputation: 86

This happens because the OmitOnRecursionBehavior you added will generate an OmitSpecimen result when it reaches the recursion limit. This works fine for properties, but does not work for constructor calls.

I think the simplest solution to your problem is to replace the OmitOnRecursionBehavior with NullRecursionBehavior.

The difference between Omit and Null is that NullRecursionBehavior will not rely on returning OmitSpecimen values, but will simply return null values at the recursion limit. This is often exactly what you want when you have recursive types.

Upvotes: 4

Related Questions