0

I have a Password class that looks like this:

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; }

}

I am trying to create tests and am using AutoFixture to help me. I am using NodaTime, which is where the IClock comes from (it gets injected), and NodaTime has a nice testing api that lets me create a FakeClock, so I set up my fixture as shown:

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);

When I test this setup in a test class with just the IClock property, my setup works just fine. But for my real Password class, I also have two customizations, one to build a valid password and one to create a valid number of days. These two customizations key on the constructor parameters, not properties. My test (so far) is as follows:

[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>();

}

And my two customizations:

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);
    }
}

The problem is that when Password is being built, the Clock property is always null, so the Build line is not populating the Clock property. As I said, If I use the same Build line against a class with just an IClock based property, the property gets populated. I'm trying to "listen" to my tests, but I'm not sure if my api is flawed, or if my test setup is just wrong. Any advice/help would be much appreciated.

Thanks!

TortillaCurtain
  • 501
  • 4
  • 18

0 Answers0