Autofixture: Question: Is there a way to generate value for only primitive properties?

Created on 18 Jul 2018  路  8Comments  路  Source: AutoFixture/AutoFixture

We are using EF Core we so designed our entity classes like the following structure:
```c#
public class Box
{
public int Id { get; set; }
public ICollection Contents { get; set; }
}

public class Content
{
public int Id { get; set; }
int BoxId { get; set; }
Box Box { get; set; }
}
`` When we are using AutoFixture to generate test data, for example:fixture.Build.Create(), it generates the value ofBoxIdand aBoxinstance with differentIdthan theBoxId(which is correct behavior BTW), however we just want AutoFixture generate all properties of primitive types and leave all properties of complex types null, currently we are using .Without() to exclude members that we don't want Autofixture to generate values for (fixture.Build.Without(c => c.Box)`), however as there are so many properties of complex types in many class, using .Without() increases our code lines.

  • Question 1: is there a way (maybe behavior) to configure this globally?
  • Question 2: Any suggestions on using AutoFixture with EF Core 2.1 and SQLite in-memory unit testing practices?
question

All 8 comments

Hi @LiangZugeng,

I'd suggest you to write a custom behavior, so it skips the non-primitive members requests. See sample (notice, I made BoxId and Box properties public, as otherwise they are ignored by AutoFixture):
```c#
public class Issue1062
{
[Fact]
public void ShouldSkipNonPrimitiveMembers()
{
var fixture = new Fixture();
fixture.Behaviors.Add(new SkipNonPrimitiveMembersBehavior());

    var content = fixture.Create<Content>();

    Assert.NotNull(content);
    Assert.NotEqual(0, content.Id);
    Assert.NotEqual(0, content.BoxId);
    Assert.Null(content.Box);

    var box = fixture.Create<Box>();
    Assert.NotEqual(0, box.Id);
    Assert.Null(box.Contents);
}

public class Box
{
    public int Id { get; set; }
    public ICollection<Content> Contents { get; set; }
}

public class Content
{
    public int Id { get; set; }
    public int BoxId { get; set; }
    public Box Box { get; set; }
}

public class SkipNonPrimitiveMembersBehavior : ISpecimenBuilderTransformation
{
    public ISpecimenBuilderNode Transform(ISpecimenBuilder builder)
    {
        return new NonPrimitiveMembersFilter(builder);
    }

    private class NonPrimitiveMembersFilter: ISpecimenBuilderNode
    {
        private readonly RequestMemberTypeResolver _memberTypeResolver = new RequestMemberTypeResolver();
        private readonly ISpecimenBuilder _innerBuilder;

        public NonPrimitiveMembersFilter(ISpecimenBuilder innerBuilder)
        {
            _innerBuilder = innerBuilder ?? throw new ArgumentNullException(nameof(innerBuilder));
        }

        public object Create(object request, ISpecimenContext context)
        {
            if(_memberTypeResolver.TryGetMemberType(request, out var memberType) && IsNonPrimitiveType(memberType))
                return new OmitSpecimen();

            return _innerBuilder.Create(request, context);
        }

        private static bool IsNonPrimitiveType(Type type)
        {
            // Improve the code
            return type != typeof(int);
        }

        public ISpecimenBuilderNode Compose(IEnumerable<ISpecimenBuilder> builders)
        {
            return new NonPrimitiveMembersFilter(new CompositeSpecimenBuilder(builders));
        }

        public IEnumerator<ISpecimenBuilder> GetEnumerator()
        {
            yield return _innerBuilder;
        }

        IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
    }
}

}
```

It should work fine, however I wouldn't recommend to apply this customization globally - you are in fact killing the primary AutoFixture 馃槦 Probably, it's better to add the behavior to the tests requiring this only.

Question 2: Any suggestions on using AutoFixture with EF Core 2.1 and SQLite in-memory unit testing practices?

I'm not aware of such suggestions unfortunately.

Thanks Alex for the code, I will give it a try, issue closed.

@zvirja The code above worked in the sample using var content = fixture.Create<Content>();, but it didn't work for the following code:

var content = fixture.Build<Content>.With(c => c.Id, 1).Create();

It still generated the Box property of the content. Please advice, thanks.

@LiangZugeng Sorry, my mistake. I tweaked the sample here so now it works fine with the Build scenario as well. More specifically, I updated the Compose() method to correctly replicate the filter.

Please test - should work as a charm now 馃槉

Great, will test it ASAP

@LiangZugeng

  • Question 2: Any suggestions on using AutoFixture with EF Core 2.1 and SQLite in-memory unit testing practices?

I found your old post, and would like to contribute my thoughts:

With AutoFixture, the main think to be careful of is cyclical references, where A is-a B and B has-a A. For example, Currency USD is-a (Tradable) Security and Security has-a (Trading) Currency. However, this can also happen with many-to-many relationships, since EFCore currently requires a CLR Type to represent the many-to-many navigation. By default, a cyclical reference will generate the following stack trace:

AutoFixture.ObjectCreationExceptionWithPath : AutoFixture was unable to create an instance of type System.RuntimeType because the traversed object graph contains a circular reference. Information about the circular path follows below. This is the correct behavior when a Fixture is equipped with a ThrowingRecursionBehavior, which is the default. This ensures that you are being made aware of circular references in your code. Your first reaction should be to redesign your API in order to get rid of all circular references. However, if this is not possible (most likely because parts or all of the API is delivered by a third party), you can replace this default behavior with a different behavior: on the Fixture instance, remove the ThrowingRecursionBehavior from Fixture.Behaviors, and instead add an instance of OmitOnRecursionBehavior:

fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList()
    .ForEach(b => fixture.Behaviors.Remove(b));
fixture.Behaviors.Add(new OmitOnRecursionBehavior());

That said, I don't understand why you would only test for primitive properties. I think you are doing things backwards, at least from a Domain-Driven Design perspective. You want to always test your navigation properties. The only reason to not do so is cyclical references.

The following projects claim to help AutoFixture with "understanding" EntityFramework models by addressing circular dependencies:

Hope that helps.

Thank you @jzabroski for your reply. The only reason why I wanted to only generate data for primitive properties was that AutoFixture would fill-in all navigation properties with random data which then caused the database operations to fail (mostly foreign key violation errors), so I came up with the idea of only generate primitive properties and manually fill-in data for all navigations properties (lots more code though).

I also checked out the LazyEntityGraph but still didn't get a chance to use it in my real project, but I will try to do it and see how it fits.

Hi James (@LiangZugeng ),

AutoFixture by definition is a powerful tool for configuring object graphs. That is what Freeze does. You just need a total ordering of all dependencies to guarantee each proxy has a unique instance.

I don't think AutoFixture is very powerful for just properties. I mean, that's an OK use for it, but the power is in the fact it is like an IoC registry plus a mocking tool.

Was this page helpful?
0 / 5 - 0 ratings