0

Hi So I have created an interface for datetime like this:

public interface ITimeProvider<T>
{
    T Now { get; }
    string ToShortDateString();
}

And then I have one implamentation of that interface like this:

public class DateTimeProvider : ITimeProvider<DateTime>
{

    private DateTime _date;

    public DateTime Now
    {
        get { return DateTime.Now; }
    }

    public  DateTimeProvider()
    {

        _date = new DateTime();
    }
    public string ToShortDateString()
    {
        return _date.ToShortDateString();
    }
}

Then I am using it in my main unit and want to use property injection but i ran into some problems, here is a snap of what I have:

public class Atm : IAtm
{
   public ITimeProvider _timeProvider { get;set; }
}

This doesn't work as I don't specifiy a type. I could just do

public ITimeProvider<DateTime> _timeProvider { get;set; }

But then I wouldn't be able to use another timeprovider. I have also considered(and is the only solution I could come up with) is to make ATM generic so something like this

 public class Atm<T> : IAtm
 {
      public ITimeProvider<T> _timeProvider { get;set; }
 }

But then I feel like I can't use property injection, is there any other way I can do this so I will be able to test it?

Sumsar1812
  • 616
  • 1
  • 9
  • 32
  • "but I ran into some problems". "doesn't work as I don't specify a type", but you have registered your implementation of the interface with the injection container I assume? – Lasse V. Karlsen Oct 29 '15 at 13:58
  • 2
    *“But then I wouldn't be able to use another timeprovider”* – How likely is it that you are considering using a different type than `DateTime` to represent dates and times? – poke Oct 29 '15 at 14:04
  • It isn't but I do want to mock it out for testing – Sumsar1812 Oct 29 '15 at 14:08
  • 2
    You only need to be able to mock `DateTime.Now` for testing (which you are with that time provider). There is no reason to encapsulate the whole `DateTime` type; you can just create the datetime objects the way you need it for testing purposes. – poke Oct 29 '15 at 14:09
  • @Sumsar1812 - Can you please explain why do you need `DateTime` to be a generic type parameter? – Channs Oct 29 '15 at 14:19
  • To me there is just an easier way. Any class that relies on DateTime.Now checks.........just have an extra constructor property called "DateTime compareDate"..........then any non unit-test code will just pass in DateTime.Now...and a unittest can pass in a coded date. I never randomly code "DateTime.Now" in my classes...it is always passed in from "above". – granadaCoder Oct 29 '15 at 14:29

2 Answers2

3

The thing you are abstracting is the DateTime.Now function not the DateTime datatype, you can keep the implementation the exactly the same but just fix the type.

public interface ITimeProvider
{
    DateTime Now { get; }
    string ToShortDateString();
}

public class DateTimeProvider : ITimeProvider
{

    public DateTime Now
    {
        get { return DateTime.Now; }
    }

    public string ToShortDateString()
    {
        return Now.ToShortDateString();
    }
}


public class RemoteTimeProvider : ITimeProvider
{
    public DateTime Now
    {
        get 
        { 
            using(var client = new WebClient())
            {
                var timeString = client.DownloadString(http://www.timeapi.org/utc/now);
                return DateTime.Parse(timeString, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal).ToLocalTime()
            }

        }
    }

    public  DateTimeProvider()
    {

    }
    public string ToShortDateString()
    {
        //ToDo
    }
}

Also, I don't really get what your ToShortDateString() does, it does not take in a parameter and it just uses the value returned from new DateTime().

Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
1

Use Fakes to be able to unit test system or other assemblies not under your control.

[TestClass]
public class TestClass1
{ 
        [TestMethod]
        public void TestCurrentYear()
        {
            int fixedYear = 2000;

            // Shims can be used only in a ShimsContext:
            using (ShimsContext.Create())
            {
                // Arrange:
                // Shim DateTime.Now to return a fixed date:
                System.Fakes.ShimDateTime.NowGet = 
                () =>
                { return new DateTime(fixedYear, 1, 1); };

                // Instantiate the component under test:
                var componentUnderTest = new MyComponent();

                // Act:
                int year = componentUnderTest.GetTheCurrentYear();

                // Assert: 
                // This will always be true if the component is working:
                Assert.AreEqual(fixedYear, year);
            }
        }
}
Ralf de Kleine
  • 11,464
  • 5
  • 45
  • 87