How do I create a specimen builder in AutoFixture to always return a string that matches a Regex?

*爱你&永不变心* 提交于 2019-12-11 02:22:00

问题


I am trying to use conventions in my unit tests which make use of AutoFixture. I have a Password value object as shown:

public class Password : SemanticType<string>
{
    private int _daysPasswordValid;

    public Password(string password) : base(IsValid, password)
    {
        Guard.NotNullOrEmpty(() => password, password);
        Guard.IsValid(() => password, password, IsValid, "Invalid Password");

        DateCreated = DateTime.Now;
    }

    static bool IsValid(string candidate)
    {
        //password cannot contain be whitespace
        if (string.IsNullOrWhiteSpace(candidate))
            return false;

        const string passwordPattern = @"^(?=.*[A-Z].*[A-Z])(?=.*[!@#$&=()-*])(?=.*[0-9].*[0-9])(?=.*[a-z].*[a-z].*[a-z]).{20}$";

        var match = Regex.Match(candidate, passwordPattern);

        return match.Success;
    }

    public DateTime DateCreated { get; private set; }

    public int DaysPasswordValid
    {
        get { return _daysPasswordValid; }
        set
        {
            const int minimunDays = 0;
            const int maximumDays = 90;

            if (value <= maximumDays && value > minimunDays)
                _daysPasswordValid = value;
            else
                throw new ArgumentOutOfRangeException(
                    "Password must be valid more than {0} and no more than {1} days.".Fmt(minimunDays, maximumDays));
        }
    }

    public static implicit operator string(Password password)
    {
        return password.Value;
    }

    public static explicit operator Password(string value)
    {
        return new Password(value);
    }
}

I would like to be able to create a fixture based on the Password class and have the fixture created with a valid password, in other words, a password that matches the RegEx pattern in the Password class. I have been looking at the RegularExpressionGenerator available in AutoFixture but haven't quite gotten things to work. Here is the builder have so far:

public class ValidPasswordBuilder : ISpecimenBuilder
{
    public ValidPasswordBuilder(string regularExpressionRequest)
    {
        PasswordRegularExpression = regularExpressionRequest;
    }

    public string PasswordRegularExpression { get; private set; }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi != null && pi.Name == "password" && pi.ParameterType == typeof(string))
        {
            var generator = new RegularExpressionGenerator();
            var regExRequest = new RegularExpressionRequest(PasswordRegularExpression);
            var result = generator.Create(regExRequest, context);
            return result.ToString();
        }

        return new NoSpecimen(request);
    }
}

Here is the test:

[Fact]
    public void ValidPasswordMatches()
    {
        var fixture = new Fixture();
        fixture.Customizations.Add(new ValidPasswordBuilder(PasswordPattern));
        Action a = () => fixture.Create<Password>();
        a.ShouldNotThrow<ArgumentException>();
    }

I can run the test as setup above, and it passes, but if I debug it, I get an error in the IsValid function in the Password class (through the Guard clause) that says that the password returned from the builder is:

Ploeh.AutoFixture.Kernel.NoSpecimen

I never seem to get a result that matches the RegEx pattern. I feel like I'm close, but need some help getting over the hump.

Thanks!


回答1:


As it looks like, the pattern you use is not supported:

^(?=.*[A-Z].*[A-Z])(?=.*[!@#$&=()-*])(?=.*[0-9].*[0-9])(?=.*[a-z].*[a-z].*[a-z]).{20}$

Try with a different Regular Expression.

Edit: Or try doing this or this, in order to configure AutoFixture to use a different Regular Expression, without changing the Password class.


AutoFixture turns Regular Expressions into Automatons by applying the algorithms of dk.brics.automaton.

You may use a different pattern or use a different engine to reverse the Regular Expression into an Automaton. As an example, you can use the Rex engine.

Though, even with Rex your Regular Expression is not supported, as the following constructs are currently not supported by Rex:

anchors \G, \b, \B, named groups, lookahead, lookbehind, as-few-times-as-possible quantifiers, backreferences, conditional alternation, substitution.


If you want to try Rex with AutoFixture you can use the following generator:

internal class RexRegularExpressionGenerator : ISpecimenBuilder
{
    public object Create(object request, ISpecimenContext context)
    {
        if (request == null)
            return new NoSpecimen();

        var regularExpressionRequest = request as RegularExpressionRequest;
        if (regularExpressionRequest == null)
            return new NoSpecimen();

        var pattern = regularExpressionRequest.Pattern;

        try
        {
            var regex = RexEngine
                .GenerateMembers(
                    new RexSettings(pattern) { k = 1 })
                .FirstOrDefault();

            if (Regex.IsMatch(regex, pattern))
                return regex;
        }
        catch (ArgumentException)
        {
            return new NoSpecimen(request);
        }

        return new NoSpecimen(request);
    }
}

As far as I can tell, you can download Rex only as a .NET application (.exe) file which you can then reference like any other managed assembly in your project.



来源:https://stackoverflow.com/questions/28706286/how-do-i-create-a-specimen-builder-in-autofixture-to-always-return-a-string-that

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!