Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AutoFixture, create a list of email addresses

I'm writing some unit tests and have a class called Account which has

public Guid AccountId {get;set;}
public IEnumerable<string> EmailAddresses {get;set;}
etc...

I want to use autofixture to create the account, but I'm having trouble getting the email format.

I have tried

fixture.Register<string>(() => string.Format("{0}@acme.com", fixture.Create<string>()));

but that that leads to circular problem.

I could do this

fixture.Register<string>(() => string.Format("{0}@acme.com", fixture.Create<int>()));

But I'd rather have a string at the start of the address.

EDIT Thanks to both answers I have a written up a summary and few other scenarios as a post here - http://nodogmablog.bryanhogan.net/2016/04/customizing-a-specific-string-inside-a-class-using-autofixture/

like image 530
Bryan Avatar asked Apr 01 '16 20:04

Bryan


2 Answers

There are a couple of ways of doing that. Here's one of them:

Assuming that MyClass is defined as

public class MyClass
{
    public Guid AccountId { get; set; }
    public IEnumerable<string> EmailAddresses { get; set; }
}

Then, a Fixture object can be customized like so

var fixture = new Fixture();
fixture.Customize<MyClass>(c => c
    .With(x =>
        x.EmailAddresses,
        fixture.CreateMany<MailAddress>().Select(x => x.Address)));

var result = fixture.Create<MyClass>();

And so the EmailAddresses will be filled with email strings that look like:

"[email protected]"
"[email protected]"
"[email protected]"
like image 56
Nikos Baxevanis Avatar answered Sep 20 '22 20:09

Nikos Baxevanis


This is one of those situations where AutoFixture is giving you feedback on the usability of your object model.

If the EmailAddresses property is supposed to only contain valid email addresses, then you should ask yourself whether representing them as generic strings is the right choice. A more specific type like the MailAddress class would restrict the set of valid values for that property. It would also make it easier to generate test data for it, since AutoFixture knows how to create instances of MailAddress.

Having said that, if it's not feasible for you to change the object model, you can still write a customization that tells AutoFixture to provide valid email addresses for any property or parameter of type IEnumerable<string> with email somewhere in their name:

public class EmailAddressStringsGenerator : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        if (IsEnumerableOfStringPropertyOrParameterNamedEmail(request))
        {
            return CreateManyEmailAddresses(context);
        }

        return new NoSpecimen();
    }

    static bool IsEnumerableOfStringPropertyOrParameterNamedEmail(object request)
    {
        return IsEnumerableOfStringPropertyNamedEmail(request) ||
               IsEnumerableOfStringParameterNamedEmail(request);
    }

    static bool IsEnumerableOfStringPropertyNamedEmail(object request)
    {
        var property = request as PropertyInfo;
        return property != null &&
               property.Name.ContainsIgnoringCase("email") &&
               typeof(IEnumerable<string>).IsAssignableFrom(property.PropertyType);
    }

    static bool IsEnumerableOfStringParameterNamedEmail(object request)
    {
        var parameter = request as ParameterInfo;
        return parameter != null &&
               parameter.Name.ContainsIgnoringCase("email") &&
               typeof(IEnumerable<string>).IsAssignableFrom(parameter.ParameterType);
    }

    static IEnumerable<string> CreateManyEmailAddresses(ISpecimenContext context)
    {
        var addresses = (IEnumerable<MailAddress>) 
            context.Resolve(typeof(IEnumerable<MailAddress>));
        return addresses.Select(a => a.Address);
    }
}

You can then use that customization in a Fixture by adding it to the Customizations property:

fixture.Customizations.Insert(0, new EmailAddressStringsGenerator());
like image 26
Enrico Campidoglio Avatar answered Sep 21 '22 20:09

Enrico Campidoglio