Autofixture: Create method of Custom ISpecimenBuilder is not called when ISpecimenBuilder.CreateMany(int count) overload is used

Created on 10 Jan 2018  路  8Comments  路  Source: AutoFixture/AutoFixture

Hi, this is related to: https://github.com/AutoFixture/AutoFixture/issues/960.

I've implemented a custom ISpecimenBuilder to generate distinct GUIDs with a given lenght:

```c#
public class UniqueShortGuidBuilder : ISpecimenBuilder
{
private readonly string propName;
private readonly int lenght;

public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
    propName = ((MemberExpression)expr.Body).Member.Name;
    this.lenght = lenght;
}

public object Create(object request, ISpecimenContext context)
{
   // Omitted for brevity
}
In my tests:

```c#
Fixture.Customizations.Add(new UniqueShortGuidBuilder<Foo>(x => x.Identifier, 6));
var fooList1 = Fixture.CreateMany<Foo>().ToList(); // This works (calls UniqueShortGuidBuilder.Create)
var fooList2 = Fixture.CreateMany<Foo>(10).ToList(); // This does not

Is this by design? Is there a workaround?

question

All 8 comments

I don't know precisely logic of your builder (as you omitted it) so it's hard to judge about reasons. But I tried to speculate the implementation and my found that my sample works fine with the latest version of AutoFixture (v4).

```c#
public class Foo
{
public string PropertyToCustomize { get; set; }
}

public class PropertyCustomization: ISpecimenBuilder
{
private static readonly PropertyInfo expectedProperty =
typeof(Foo).GetProperty(nameof(Foo.PropertyToCustomize));

public object Create(object request, ISpecimenContext context)
{
    if (expectedProperty.Equals(request))
    {
        return "42";
    }

    return new NoSpecimen();
}

}

[Fact]
public void TestCustomizationAndMany()
{
var fixture = new Fixture();
fixture.Customizations.Add(new PropertyCustomization());

var result1 = fixture.CreateMany<Foo>().ToList();
var result2 = fixture.CreateMany<Foo>(10).ToList();

Assert.All(result1, v => Assert.Equal("42", v.PropertyToCustomize));
Assert.All(result2, v => Assert.Equal("42", v.PropertyToCustomize));

}
```

Please review my sample and clarify the difference between it and your logic. Also try to use the AutoFixture v4 - it could happen that you face some issue that has been already solved 馃槈

@zvirja Thanks for the response, I have some questions though:

I don't know precisely logic of your builder (as you omitted it) so it's hard to judge about reasons. But I tried to speculate the implementation and my found that my sample works fine with the latest version of AutoFixture (v4).

The problem has nothing to do with logic inside the Create method (Like I've described it is not even called) so is safe to assume this does not play a part in the solution (or even in the problem).
Another thing is that your "repro" is much simpler than mine since you're not using generics. (I was suspecting it could be something related to that, but I can't tell precisely).

Please review my sample and clarify the difference between it and your logic. Also try to use the AutoFixture v4 - it could happen that you face some issue that has been already solved

I'm definitely going to do that although I have just installed AutoFixture - and I'm believing it is the last version (or at least it should have installed the last one). 馃槈 馃憤

Another thing is that your "repro" is much simpler than mine since you're not using generics.

Generics usage should not change behavior somehow as far as I know the code, therefore my sample should look equivalent 馃槙

I have just installed AutoFixture - and I'm believing it is the last version (or at least it should have installed the last one).

Well, v4 has been released a few hours ago so it could easily happened that you installed the v3 馃槃


I could advise you to start troubleshooting by complicating my sample till the issue starts to reproduce. Alternatively, you could simplify your code to create a minimal code example, but that might be much harder.. I don't see the obvious reason so far to avoid that way 馃槥

@zvirja Hi, thanks again for the quick response.
I found the problem and it has to do with customizations registration order...
I've read something about it in @ploeh's blog.

Well, v4 has been released a few hours ago so it could easily happened that you installed the v3

About that, I thought I had the last version because of the v4 Wiki page (I assumed it was the last version then) 馃榿.

So, what I was doing and what was the solution:

```c#
Fixture.Customize(new FooCustomization());
Fixture.Customizations.Add(new UniqueShortGuidBuilder(x => x.Identifier, 6)); // ctor is called but execution is "overriden" by the line above
var foos = Fixture.CreateMany(10).ToList(); // Does not work - UniqueShortGuidBuilder.Create() is not called


**FooCustomization:**

```c#
public class FooCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var specimen = fixture.Build<Foo>()
            .OmitAutoProperties()
            .With(x => x.Identifier)
            .Create();

        fixture.Register(() => specimen);
    }
}

UniqueShortGuidBuilder:

```c#
public class UniqueShortGuidBuilder : ISpecimenBuilder
{
private readonly string propName;
private readonly int lenght;

public UniqueShortGuidBuilder(Expression<Func<TEntity, string>> expr, int lenght)
{
    propName = ((MemberExpression)expr.Body).Member.Name;
    this.lenght = lenght;
}

public object Create(object request, ISpecimenContext context)
{
   // Omitted for brevity
}

Now, I'm registering every single implementation of `ISpecimenBuilder` before the implementations of `ICustomization`:

```c#
Fixture.Customizations.Add(new UniqueShortGuidBuilder<Foo>(x => x.Identifier, 6));
Fixture.Customize(new FooCustomization());
var foos = Fixture.CreateMany<Foo>(10).ToList(); // Works as intended - UniqueShortGuidBuilder.Create() is called normally

Thanks for sharing your sample!

Indeed, the customization order does matter as we follow the chain-of-responsibility pattern. As soon as entry is matched, other builders are not even asked. However, it seems you don't need further explanation as you already got the idea 馃槄

P.S. I'd refactor the FooCustomization as following (to make it more concise):

```c#
public class FooCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
fixture.Freeze(c => c
.OmitAutoProperties()
.With(x => x.Identifier)
);
}
}

or like following (if you don't need to make the created `Empresa` instance singleton):

```c#
public class FooCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Customize<Empresa>(c => c
            .OmitAutoProperties()
            .With(x => x.Identifier)
        );
    }
}

@zvirja Yep, thanks a lot... Your support was fundamental here 馃槈.

P.S: About the refactoring using fixture.Freeze or fixture.Customize. What did you mean by _"more concise"_? What changes exactly? (I'm asking because this project seems to rely more on SO and @ploeh's blog for documentation, so sometimes is hard to find exactly what you're looking for 馃槄).

What did you mean by "more concise"? What changes exactly?

There is no difference between the following two snippets:
```c#
public class FooCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
var specimen = fixture.Build()
.OmitAutoProperties()
.With(x => x.Identifier)
.Create();

    fixture.Register(() => specimen);
}

}

and
```c#
public class FooCustomization : ICustomization
{
    public void Customize(IFixture fixture)
    {
        fixture.Freeze<Empresa>(c => c
            .OmitAutoProperties()
            .With(x => x.Identifier)
        );
    }
}

Basically, both them do exactly the same - create a customized instance of the Empresa object and configure fixture to always return that object when instance of Empresa is requested. Notice, with such configuration fixture will always return the _same object_ without creation of new ones. That's why the method is called Freeze - it freezes the object.

If you don't need that "freezing" behavior and would like to get a new object for each time, you can modify customization as following:
c# public class FooCustomization : ICustomization { public void Customize(IFixture fixture) { fixture.Customize<Empresa>(c => c .OmitAutoProperties() .With(x => x.Identifier) ); } }

In this case you still configure how exactly to construct object, however don't pin the particular instance, so a new one is created for each time.

Both the options are perfectly fine and you should evaluate your scenario to find which exact one you need.

... rely more on SO and @ploeh's blog for documentation ...

Yep, for now that is so. However I have strong plans to create a doc site in the nearby future, so all the API will be described with samples.

@zvirja Thanks a lot for the detailed explanation!

Yep, for now that is so. However I have strong plans to create a doc site in the nearby future, so all the API will be described with samples.

That will be really really helpfull 馃槃.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

mjfreelancing picture mjfreelancing  路  4Comments

tomasaschan picture tomasaschan  路  3Comments

ploeh picture ploeh  路  3Comments

tiesmaster picture tiesmaster  路  7Comments

zvirja picture zvirja  路  3Comments