Autofixture: Approach to generate Random specimen based on Customization

Created on 8 Jan 2018  ·  11Comments  ·  Source: AutoFixture/AutoFixture

Hi, I want to be able to generate distinct values based on a ICustomization using ISpecimenBuilder.CreateMany. I was wondering what would be the best solution since AutoFixture will generate the same values for all entities.

```c#
public class FooCustomization : ICustomization
{
public void Customize(IFixture fixture)
{
var specimen = fixture.Build()
.OmitAutoProperties()
.With(x => x.CreationDate, DateTime.Now)
.With(x => x.Identifier, Guid.NewGuid().ToString().Substring(0, 6)) // Should gen distinct values
.With(x => x.Mail, $"[email protected]")
.Create();

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

}
```

PS.: The main goal is to reduce the amount of code on my tests, so I must avoid calling With() to customize the values for every test. Is there a proper way of doing this?

Update: I just read this response from @ploeh: https://github.com/AutoFixture/AutoFixture/issues/361#issuecomment-86873060 that might be what I'm looking for.
This approach has many drawbacks: First it seems really counter intuitive to call Create<List<Foo>>() because it kinda of defeats the purpose of what to expect from CreateMany<Foo>; that would generate a hardcoded sized List> (?). Another drawback is that I'd have to have two customizations for each entity; one to create custom collections and another one to create a single instance, since we are overriding the behaviour of Create<T> to create a collections.

question

All 11 comments

This seems like a good candidate for a Stack Overflow question. There's nothing wrong with asking a question here, but I think this question is of the sort where an answer might benefit other people as well, and it's easier to find questions and answers on Stack Overflow compared to GitHub issues. For one, Google does a good job of indexing Stack Overflow questions, whereas it seems that it ranks (closed) GitHub issues lower...

@ploeh I agree with you....
I've just created an SO question based on this Issue. This is the link for future references 👍

@ploeh Thanks for answering the question on SO :blush:

@thiagomajesk Actually there are no issues with answering the questions here. The reason why Mark suggested SO is the search capabilities it offers. Also currently Mark is monitoring SO, so if you post a question there, there are more chances to get his expertise (like you got this time) :sweat_smile:

@zvirja Thanks for clarifying that 😄

@zvirja I'm going to close the question on SO based on @ploeh's great answer.
But I'd like to ask for a little bit more information about the implementation details on this one.
As I don't want to digress even further from the subject I think would be better to ask about this specifics in here. So I have this implementation:

```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)
{
var pi = request as PropertyInfo;

if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
    return new NoSpecimen();

return Guid.NewGuid().ToString().Substring(0, lenght);

}
```

I was expecting that when creating many objects with AutoFixture, the ISpecimenBuilder.Create would be called multiple times, generating distinct Guids for my entities; but apparently this is not true and they're actually sharing the same one.

Well, I've just tested your code and it worked exactly how desired:

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

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)
{
    var pi = request as PropertyInfo;

    if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
        return new NoSpecimen();

    return Guid.NewGuid().ToString().Substring(0, lenght);
}

}

[Fact]
public void TestCustomization()
{
var fixture = new Fixture();
fixture.Customizations.Add(new UniqueShortGuidBuilder(x => x.PropertyToCustomize, 5));

var manyFoos = fixture.CreateMany<Foo>(10);

var uniqueIds = manyFoos
    .Select(x => x.PropertyToCustomize)
    .Where(x => x.Length == 5)
    .Distinct()
    .ToArray();
Assert.Equal(10, uniqueIds.Length);

}
```

Therefore, I'd like to again ask you to see how your actual code differs, so we could understand why that difference does matter 😟

@zvirja That's weird. The code bellow is a minimal repro of the problem I'm having.
If I remove the Fixture.Customize(new FooCustomization()); the test will pass, otherwise not.

```c#
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using AutoFixture.Kernel;
using System.Linq.Expressions;
using System.Reflection;
using AutoFixture;
using System.Linq;

namespace UnitTestProject1
{
public class Foo
{
public string PropertyToCustomize { get; set; }
}

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

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

public class UniqueShortGuidBuilder<TEntity> : 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)
    {
        var pi = request as PropertyInfo;

        if (pi == null || pi.PropertyType != typeof(string) || pi.Name != propName)
            return new NoSpecimen();

        return Guid.NewGuid().ToString().Substring(0, lenght);
    }
}

[TestClass]
public class UnitTest1
{
    public static Fixture Fixture { get; private set; }

    [ClassInitialize]
    public static void ClassInitialize(TestContext context)
    {
        Fixture = new Fixture();
        Fixture.Customizations.Add(new UniqueShortGuidBuilder<Foo>(x => x.PropertyToCustomize, 5));
        Fixture.Customize(new FooCustomization());
    }

    [TestMethod]
    public void TestMethod1()
    {
        var manyFoos = Fixture.CreateMany<Foo>(10);

        var uniqueIds = manyFoos
            .Select(x => x.PropertyToCustomize)
            .Where(x => x.Length == 5)
            .Distinct()
            .ToArray();

        Assert.AreEqual(10, uniqueIds.Length);
    }
}

}
```

Recommendation: If you rely on taking the first _n_ characters off a GUID string, don't name the class UniqueShortGuidBuilder. The values may be fairly unique when length is 5, but if you set it to 1, you'll likely see collisions. In any case, uniqueness isn't guaranteed.

If you only care about the length of the string, plus non-guaranteed uniqueness, why not simply ask AutoFixture for a number between, say, 0 and 99,999, and turn it into a string? IIRC, numbers are unique until the range is depleted.

If I remove the Fixture.Customize(new FooCustomization()); the test will pass, otherwise not.

Well, in fact that is the expected behavior if you review the situation more carefully 😅 Please see my reply in #962. The provided implementation of the FooCustomization creates instance of Foo type and configures fixture to always return that instance. Therefore, when later you create multiple instances of Foo type, each time the same object is returned and your builder is not further invoked.

If you want a new instance to be created for each time you request Foo, use the Customize<> API as I suggested here.

@ploeh Thanks for the tip, but that code is just a gross example for test purposes. For real cases the lenght will not be ridiculously short to affect Guids uniqueness 😄.

If you only care about the length of the string, plus non-guaranteed uniqueness, why not simply ask AutoFixture for a number between, say, 0 and 99,999, and turn it into a string?

I bet this is a nice solution for most cases, but I can't do that for this specific case because of alphanumeric validations in my system. Besides that, I have more complex validations involving that field.

Well, in fact that is the expected behavior if you review the situation more carefully

@zvirja It is clear after your reply in #962. But for me that behavior was not obvious, since there is not upfront (explicit) documentation about AutoFixture (like I pointed out here) 😁.

To wrap up, thank you both for the patience and all the excelent explanations.
I'm sure this will be usefull for others in the future too 😉.

Okay, fair enough, I may have a tendency to take things too literal 😄

Was this page helpful?
0 / 5 - 0 ratings

Related issues

zvirja picture zvirja  ·  4Comments

DeafLight picture DeafLight  ·  5Comments

Accc99 picture Accc99  ·  4Comments

Ephasme picture Ephasme  ·  3Comments

ploeh picture ploeh  ·  3Comments