如何为属性和构造函数参数自定义AutoFixture fixture?

时间:2015-08-24 13:14:33

标签: c# unit-testing xunit.net autofixture

我有一个如下所示的密码类:

public class Password : SemanticType<string>
{
    public Password(string password, int daysValid) : base(IsValidPassword, password)
    {
        Guard.NotNullOrEmpty(() => password, password);
        Guard.IsValid(() => password, password, IsValidPassword, "Invalid Password");
        Guard.IsValid(() => daysValid, daysValid, IsValidDays, "Invalid number of days");

        DaysPasswordValid = daysValid;
        SetPasswordExpiration(daysValid);
    }

    private void SetPasswordExpiration(int daysValid)
    {
        var duration = Duration.FromStandardDays(daysValid);
        ExpiryInstant = Clock.Now.Plus(duration);
    }

    public IClock Clock { get; set; }

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

        return PasswordPolicy.IsValid(candidate);
    }

    private static bool IsValidDays(int days)
    {
        const int minimunDays = 1;
        const int maximumDays = 90;

        return days <= maximumDays && days >= minimunDays;
    }

    public Instant ExpiryInstant { get; private set; }

    public bool HasExpired
    {
        get { return Clock.Now > ExpiryInstant; }
    }

    public int DaysPasswordValid { get; private set; }

}

我正在尝试创建测试并使用AutoFixture来帮助我。我正在使用NodaTime,这是IClock的来源(它被注入),NodaTime有一个很好的测试api,可以让我创建一个FakeClock,所以我设置我的夹具如图所示:

static readonly Instant TestInstant = Instant.FromUtc(2015, 01, 01, 01, 01, 01);
readonly FakeClock _stubClock = new FakeClock(TestInstant);
var fixture = new Fixture();
fixture.Build<Password>().With(p => p.Clock, _stubClock);

当我在仅具有IClock属性的测试类中测试此设置时,我的设置工作正常。但对于我真正的密码类,我还有两个自定义项,一个用于构建有效密码,另一个用于创建有效天数。这两个自定义关键在构造函数参数上,而不是属性。我的测试(到目前为止)如下:

[Fact]
public void TestBuilder()
{
    var fixture = new Fixture();
    fixture.Build<Password>().With(p => p.Clock, _stubClock);
    fixture.Customizations.Add(new PasswordBuilder(_allValidRules, MinimumPasswordLength));
    fixture.Customizations.Add(new ValidDaysParameterBuilder(MinimumDaysValid, MaximumDaysValid));
    var pwd = fixture.Create<Password>();

}

我的两个自定义:

public class PasswordBuilder : ISpecimenBuilder
{
    public IList<string> Rules { get; private set; }
    public int ValidLength { get; private set; }

    public PasswordBuilder(IList<string> rules, int validLength)
    {
        if (rules == null) throw new ArgumentNullException("rules");
        Rules = rules;
        ValidLength = validLength;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi != null && pi.Name == "password" && pi.ParameterType == typeof(string))
        {
            var temp = string.Empty;

            foreach (var rule in Rules)
            {
                var generator = new RegularExpressionGenerator();
                var regExRequest = new RegularExpressionRequest(rule);
                var result = generator.Create(regExRequest, new DelegatingSpecimenContext());
                temp += result.ToString();
            }
            if (string.IsNullOrEmpty(temp))
                return new NoSpecimen(request);

            if (temp.Length < ValidLength)
                temp = temp.PadRight(ValidLength, 'x');
            return temp;
        }

        return new NoSpecimen(request);
    }

    private class DelegatingSpecimenContext : ISpecimenContext
    {
        public DelegatingSpecimenContext()
        {
            this.OnResolve = r => null;
        }

        public object Resolve(object request)
        {
            return this.OnResolve(request);
        }

        private Func<object, object> OnResolve { get; set; }
    }

public class ValidDaysParameterBuilder : ISpecimenBuilder
{
    private readonly int _minValue;
    private readonly int _maxValue;

    public ValidDaysParameterBuilder(int minValue, int maxValue)
    {
        _minValue = minValue;
        _maxValue = maxValue;
    }

    public object Create(object request, ISpecimenContext context)
    {
        var pi = request as ParameterInfo;
        if (pi != null && pi.Name == "daysValid" && pi.ParameterType == typeof (int))
        {
            return context.Resolve(new RangedNumberRequest(typeof(int), _minValue, _maxValue));
        }

        return new NoSpecimen(request);
    }
}

问题是,在构建Password时,Clock属性始终为null,因此Build行不会填充Clock属性。正如我所说,如果我对一个只有基于IClock的属性的类使用相同的Build行,则会填充该属性。我试图&#34;听&#34;我的测试,但我不确定我的api是否有缺陷,或者我的测试设置是否错误。任何建议/帮助将不胜感激。

谢谢!

0 个答案:

没有答案