Can AutoFixture generate random strings/text from a provided data set?

前端 未结 1 844
旧巷少年郎
旧巷少年郎 2021-01-15 12:50

It is possible to use AutoFixture to generate random data for a string property .. but it\'s from a fixed data source?

For example: I have 30 street names hardcoded

相关标签:
1条回答
  • 2021-01-15 13:08

    As with so many other things regarding AutoFixture, things become much easier if you can use more explicit domain modelling. Instead of modelling StreetName as a string, introduce a domain object for it:

    public sealed class StreetName
    {
        private readonly string value;
    
        public StreetName(string streetName)
        {
            value = streetName ?? throw new ArgumentNullException(nameof(streetName));
        }
    
        public override bool Equals(object obj)
        {
            var other = obj as StreetName;
            if (other == null)
                return base.Equals(obj);
    
            return Equals(value, other.value);
        }
    
        public override int GetHashCode()
        {
            return value.GetHashCode();
        }
    
        public override string ToString()
        {
            return value;
        }
    
        public static implicit operator string(StreetName streetAddress)
        {
            return streetAddress.value;
        }
    
        public static implicit operator StreetName(string streetAddress)
        {
            return new StreetName(streetAddress);
        }
    }
    

    This is one of those modelling steps that are painful in C# and Java, but would be a one-liner in F# or Haskell...

    Let's assume, however, that we have a list of predefined street names:

    public static class StreetNames
    {
        public static IEnumerable<string> Values = new[] {
            "221 B Baker St.",
            "1313 Webfoot Walk",
            "420 Paper St.",
            "42 Wallaby Way"
            /* More addresses go here... */ };
    }
    

    You can now trivially tell AutoFixture to pick only from that list, using ElementsBuilder:

    var fixture = new Fixture();
    fixture.Customizations.Add(
        new ElementsBuilder<StreetName>(StreetNames.Values.Select(s => (StreetName)s)));
    

    At this point, though, it means that when you create StreetName values with AutoFixture, it'll pick from StreetNames.Values, but it still isn't going to do that when you ask it to create Address values. You can address (ha ha) that issue with a little ISpecimenBuilder:

    public class StreetNameBuilder : ISpecimenBuilder
    {
        public object Create(object request, ISpecimenContext context)
        {
            var pi = request as PropertyInfo;
            if (pi == null || pi.Name != "StreetName" || pi.PropertyType != typeof(string))
                return new NoSpecimen();
    
            var sn = context.Resolve(typeof(StreetName));
            return (string)(StreetName)sn;
        }
    }
    

    Now you can configure your Fixture like this:

    var fixture = new Fixture();
    fixture.Customizations.Add(
        new ElementsBuilder<StreetName>(StreetNames.Values.Select(s => (StreetName)s)));
    fixture.Customizations.Add(new StreetNameBuilder());
    

    It'll now create Address values with StreetName values picked from the predefined list.

    If you can't change your domain model, you can still add a class like StreetName. Just add it to your test code base instead of the production code base.

    0 讨论(0)
提交回复
热议问题