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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With