Search code examples
c#autofixture

Autofixture rule for properties


I'm writing integrational test to my UserController. And currently testing User creation method using AutoFixture. CreateUser method accepts CreateUserDto

public class CreateUserDto : IMapWith<User>
    {
        public string Username { get; init; }
        public string Name { get; init; }
        public string Surname { get; init; }
        public DateTime BirthDay { get; init; }
        public UserTypeId UserTypeId { get; init; }
        public Guid CityId { get; init; }
        public string Email { get; init; }
        public string PhoneNumber { get; init; }
        public Guid OrientationId { get; init; }
        public Guid GenderId { get; init; }

        public void Mapping(Profile profile)
        {
            profile.CreateMap<CreateUserDto, User>();
        }
    }

And i want to set Email to have and email look and PhoneNumber to have only 11 digits. For email i tried this but it's not working it still generates guid instead of email:

var fixture = GetFixture();
            fixture.Customizations.Add(new MailAddressGenerator());
var userDto = fixture.Create<CreateUserDto>();

I don't even know how to customize number.

EDIT

I tried to create it with custom customization:

internal class EmailAndPhoneNumberCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Customize<CreateUserDto>(c => c
                .With(u => u.Email, "[email protected]")
                .With(u => u.PhoneNumber, "87777777777"));
        }
    }

And it's ignoring it. Code where i use it:

private async Task<IEnumerable<UserEntity>> GenerateUsers()
        {
            var fixture = GetFixture();

            var users = fixture.CreateMany<UserEntity>(3);

            return users;
        }

        private static Fixture GetFixture()
        {
            var fixture = new Fixture();
            fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList().ForEach(behavior => fixture.Behaviors.Remove(behavior));
            fixture.Behaviors.Add(new OmitOnRecursionBehavior());
            fixture.Customize(new EmailAndPhoneNumberCustomization());
            return fixture;
        }

Solution

  • As mentioned in the comments the generic type in CreateMany<>(3) is wrong, should be the customized type. As for making it easier, depending on your testing framework you can make use of data attributes and inject the values into your test method. To make data attribute creation easier, you can encapsulate the customization using the ICustomization interface.

    The example below demonstrates how to encapsulate the customization into reusable classes and how to generate mail addresses and phone numbers.

    /* recursion omitter customization */
    public class OmitRecursionCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Behaviors.OfType<ThrowingRecursionBehavior>()
                .ToList().ForEach(behavior => fixture.Behaviors.Remove(behavior));
            fixture.Behaviors.Add(new OmitOnRecursionBehavior());
        }
    }
    
    /* your DTO customizations */
    public class DtoCustomization : ICustomization
    {
        public void Customize(IFixture fixture)
        {
            fixture.Customize<CreateUserDto>(c => c
                    .With(u => u.Email, fixture.Create<MailAddress>().Address)
                    .With(u => u.PhoneNumber, fixture.Create(
                        new RegularExpressionRequest("[1-9]{11}"),
                        new SpecimenContext(fixture))));
        }
    }
    
    /* The data attribute customization */
    public class DomainDataAttribute : AutoDataAttribute
    {
        public DomainDataAttribute()
            : base(() => new Fixture().Customize(
                new CompositeCustomization(
                    new OmitRecursionCustomization(),
                    new DtoCustomization())))
        {
        }
    }
    
    /* The test */
    [Theory, DomainData]
    public void Foo(List<CreateUserDto> users)
    {
        Assert.Equal(3, users.Count);
        Assert.All(users, user => Assert.Matches(@"\@example\.(com|org|net)", user.Email));
        Assert.All(users, user => Assert.Matches("[1-9]{11}", user.PhoneNumber));
    }