Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoFixture creating unwanted depth in complex objects

I have a complex graph, from EF6 (database-first), that I want to simulate using mocks and test fixtures while testing my domain model. To prevent the problems with circular dependencies, I've added these lines to the test setup:

private Fixture _fixture;
public WhenRetrievingPlans()
{
    _fixture = new Fixture();
    _fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
                            .ForEach(b => _fixture.Behaviors.Remove(b)); 
    _fixture.Behaviors.Add(new OmitOnRecursionBehavior());
    using (var writer = new StreamWriter(@"D:\Workspace\Project\source\Project.Model.Test\trace.txt"))
    {
        _fixture.Behaviors.Add(new TracingBehavior(writer));

and then I try to prevent the fixture from trying to chase the object graph into oblivion, and just create a simple object without any children, like so:

        _fixture.Build<EntityType>()
            .Without(e => e.ObjectProperty1)
            .Without(e => e.ObjectProperty2)
            .Without(e => e.CollectionProperty3)
            .Without(e => e.CollectionProperty4)
            .Without(e => e.ObjectProperty5)
            ...
            .Create();

So that no objects or collections which are properties of this EntityType are created. The remaining properties are simple types, including some Nullable and DateTime values as well as Int32 and String types.

I want to have AutoFixture create an instance of EntityType, and then I plan to return that from a Mock. (This might not be relevant.)

        var entitiesDbSetMock = new Mock<IDbSet<EntityType>>();
        entitiesDbSetMock.SetupAllProperties();
        _fixture.Inject<IDbSet<EntityType>>(entitiesDbSetMock.Object);

Then I add the object to this Mock:

        var entity = _fixture.Create<EntityType>();
        entitiesDbSetMock.Object.Attach(entity);

The problem is this: AutoFixture is not stopping itself from generating every possible object that can be reached from this EntityType, even with all the .Without() operations I used. How do I know? Because I turned on tracing. Just for this one Create operation, nearly 750,000 lines of trace is generated (it's a 100MB file).

In reading the trace, the first section shows the creation of the EntityType and the setting of all the properties that I didn't exclude. But immediately after that thing gets create, it seems to get created again, but this time it ignores my Without() customizations, and goes deep.

Requested: Ploeh.AutoFixture.Kernel.SeededRequest
  Requested: Project.Data.EntityType
    Requested: Int32 EntityTypeID
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: System.Int32
        Created: 232
      Created: 232
    Created: 232
    Requested: System.Nullable`1[System.Int32] ForeignKey1ID
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: System.Nullable`1[System.Int32]
          Requested: Int32 value
            Requested: Ploeh.AutoFixture.Kernel.SeededRequest
              Requested: System.Int32
              Created: 224
            Created: 224
          Created: 224
        Created: 224
      Created: 224
    Created: 224
    Requested: Int32 Property2ID
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: System.Int32
        Created: 40
      Created: 40
    Created: 40
    Requested: System.DateTime Property3Date
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: System.DateTime
        Created: 6/6/2014 4:05:24 PM
      Created: 6/6/2014 4:05:24 PM
    Created: 6/6/2014 4:05:24 PM
    *... (similar lines elided for brevity)*
    Requested: Project.Data.OtherEntity OtherEntity
    Created: Ploeh.AutoFixture.Kernel.OmitSpecimen
    Requested: Project.Data.YetAnotherEntity YetAnotherEntity
    Created: Ploeh.AutoFixture.Kernel.OmitSpecimen
    Requested: System.Collections.Generic.ICollection`1[Project.Data.CollectionEntity] CollectionEntities
    Created: Ploeh.AutoFixture.Kernel.OmitSpecimen
    *... (similar lines elided for brevity)*
  Created: Project.Data.EntityType
Created: Project.Data.EntityType

Right there, I expect the trace to be done. The object got created, exactly how I wanted. But it doesn't stop there. Immediate following that, I see AutoFixture keeps going.

Requested: Ploeh.AutoFixture.Kernel.SeededRequest
  Requested: Project.Data.YetAnotherEntity
  Created: Project.Data.YetAnotherEntity
Created: Project.Data.YetAnotherEntity
Requested: Ploeh.AutoFixture.Kernel.SeededRequest
  Requested: Project.Data.UnrelatedEntity
  Created: Project.Data.UnrelatedEntity
Created: Project.Data.UnrelatedEntity
Requested: Ploeh.AutoFixture.Kernel.SeededRequest
  Requested: Project.Data.EntityType  *<<-- Again? We did this already.*
    Requested: Int32 EntityTypeID
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: System.Int32
        Created: 107
      Created: 107
    Created: 107
    Requested: System.Nullable`1[System.Int32] ForeignKey1ID
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: System.Nullable`1[System.Int32]
          Requested: Int32 value
            Requested: Ploeh.AutoFixture.Kernel.SeededRequest
              Requested: System.Int32
              Created: 87
            Created: 87
          Created: 87
        Created: 87
      Created: 87
    Created: 87
    Requested: Int32 Property2ID
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: System.Int32
        Created: 105
      Created: 105
    Created: 105
    Requested: System.DateTime Property3Date
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: System.DateTime
        Created: 5/31/2014 6:50:10 PM
      Created: 5/31/2014 6:50:10 PM
    Created: 5/31/2014 6:50:10 PM
    Requested: Project.Data.OtherEntity OtherEntity  *<<!! NO! I stopped you!*
      Requested: Ploeh.AutoFixture.Kernel.SeededRequest
        Requested: Project.Data.OtherEntity
          Requested: Int32 ForeignKey1ID
            Requested: Ploeh.AutoFixture.Kernel.SeededRequest
              Requested: System.Int32
              Created: 168
            Created: 168
          Created: 168
          Requested: System.String Name
            Requested: Ploeh.AutoFixture.Kernel.SeededRequest
              Requested: System.String
              Created: 61eb26e3-890d-4d0d-badd-cd3467c299ed
            Created: Name61eb26e3-890d-4d0d-badd-cd3467c299ed
          Created: Name61eb26e3-890d-4d0d-badd-cd3467c299ed
          Requested: Int32 OtherEntityID
            Requested: Ploeh.AutoFixture.Kernel.SeededRequest
              Requested: System.Int32
              Created: 187
            Created: 187
          Created: 187
*... and on and on for 750,000 more lines.

Sure, this things probably has all kinds of code smells, and I know that Mark Seemann is not big on EF or any other ORM. But I think it should work, and I can't figure out what I missed.

like image 836
Alan McBee Avatar asked Oct 19 '22 08:10

Alan McBee


1 Answers

And now that I look at the examples more closely in order to answer Mark's comment (thank you, Mark!!) ... I see that there's a return value of type T for the Build method. (smacks forehead).

So now I see I was wrong in what I thought Build does. It actually instantiates a T right there. I suspect what I would have needed to do (to create a template) was to extend SpecimenBuilder and register that somehow, by adding it in at the Customize/Customizations sequence of setting up the Fixture.

Mark, if you want to leave a better answer, I'll wait a day or two and then pick your answer as the correct answer.

like image 154
Alan McBee Avatar answered Oct 29 '22 23:10

Alan McBee