-
Notifications
You must be signed in to change notification settings - Fork 337
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add support for string based properties decorated with the RangeAttribute #919
Comments
Originated from https://stackoverflow.com/q/47184400/126014 |
@jcasale Thanks for raising the issue. Well, as far as I noticed, AutoFixture wasn't designed to handle gracefully the I don't have too much experience with annotation attributes and at the first glance it looks a bit weird that you apply numeric range attribute to a property of P.S. 👍 for using AutoFixture v4 😉 |
To be honest, it's a convention I am used to from the ASP arena and I don't know if it's an abuse of the facility or intended. I will check out the docs and if not, the reference source for logic that indicates it's valid and expected and not simply a fragile coincidence. |
Reference source implies it will handle casting a string (which it does) and the docs illustrate an example where the the value being applied is coerced and in that case, boxed. I have a workaround and I admit the use case is rare, however it does appear to be valid but certainly low priority if any. |
@jcasale Thanks for the confirmation. Well, it's indeed seems that string could be a valid type. Also it looks like that Probably, it makes sense to follow the way, when we support this feature partially - recognize the @jcasale What would you say about that plan? |
The purpose of supporting data annotations in AutoFixture is to provide a more fine-grained control over the scope of generated values. However, some of those data annotations have a totally weird API, where you can easily do the wrong thing, as with the RangeAttribute, which has 3 constructor overloads:
And because of that pesky 3rd constructor overload accepting a [Range(1, long.MaxValue)]
public long SomeProperty { get; set; }
So, IMHO, and AFAICT, in this case: [Range(1, 65535)]
public string Port { get; set; } we should throw an error. In the error message we should probably tell to the user that the right way of controlling the scope of generated strings is by doing this: [Range(typeof(string), "1", "65535")]
public string Port { get; set; } And then, we'd have to make sure we support not only strings, but chars, dates, and so on: [Range(typeof(DateTime),"14-Dec-1984", "14-Dec-2099")] [Range(typeof(char), "n", "s")] This is one of the reasons that F# Hedgehog makes this concept explicit, and easier, through the |
[Range(typeof(string), "1", "65535")]
public string Port { get; set; } That is incorrect, it works coincidentally for values where none of the leading place values exceed the leading place values of the max string. For example, A contrived example: using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
internal class Program
{
[Range(typeof(string), "1", "65535")]
// [Range(1, 65535)]
public string Port { get; set; }
private static void Main()
{
for (int i = 1; i < 65536; i++)
{
Program program = new Program
{
Port = i.ToString()
};
foreach (var error in program.Validate())
{
Console.WriteLine($"{i}, {error.ErrorMessage}");
}
}
}
}
public static class Extensions
{
public static IEnumerable<ValidationResult> Validate<T>(this T model)
where T : class
{
if (model == null)
{
throw new ArgumentNullException(nameof(model));
}
foreach (PropertyInfo propertyInfo in model.GetType().GetProperties())
{
object[] attributes = propertyInfo.GetCustomAttributes(typeof(ValidationAttribute), false);
if (attributes.Length == 0)
{
continue;
}
ValidationContext validationContext = new ValidationContext(model)
{
DisplayName = propertyInfo.Name
};
if (attributes.OfType<RequiredAttribute>().FirstOrDefault() is RequiredAttribute required)
{
ValidationResult result = required.GetValidationResult(propertyInfo.GetValue(model), validationContext);
if (result != null)
{
yield return result;
yield break;
}
}
foreach (ValidationAttribute attribute in attributes)
{
ValidationResult result = attribute.GetValidationResult(propertyInfo.GetValue(model), validationContext);
if (result == null)
{
continue;
}
yield return result;
}
}
}
} |
@jcasale Thanks for the sample. That's why I'd suggest to not include this feature to the AutoFixture for now as there might be a whole set of different options depending on the In the #920 I've introduced the generic Later @jcasale could register it's own For me that looks like a good trade-off. |
@zvirja No objections, I have a workaround and realized some good takeaways from this. In reference to registering my own |
Probably, not for now, as #920 is still under the review by @moodmosaic. Only after we merge the PR, we'll know its shape, so I'll be able to show you a demo. However, the best place would be to look at the NumericRangedRequestRelay implementation (if it doesn't changes during the review) as it's a sample of builder for numeric types. |
@jcasale Feel free to use the NumericRangedRequestRelay or EnumRangedRequestRelay as a sample of such customization. This API will be available since our next release that should happen in the nearby future. Closing this one as no further action is required so far. Feel free to ask more questions if you have 😉 |
Consider the following:
In 3.51.0 the first call the Create() worked, however subsequent calls threw, now in 4.0.0-rc1 (the version I am using), the first call throws
ObjectCreationException
whereas the attribute itself offers support for the use case.The text was updated successfully, but these errors were encountered: