Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct way of unit-testing classes that use DateTimeOffset objects?

I would appreciate information or examples about how to correctly test code that uses DateTimeOffset instances. I know the tests have to be deterministic.

So, how would one isolate the application from the DateTimeOffset classes ? I would, of course, like to be able to use a fake DateTimeOffset.Now, etc.

In my tests, should I be using something like:

var myDate = new DateTimeOffset(2016, 3, 29, 12, 20, 35, 93, TimeSpan.FromHours(-3));

Or would I instead be using a wrapper class like MyCustomDateTimeOffset ? Should I not use DateTimeOffset at all in my code and use a wrapper instead?

like image 367
Stephen H. Anderson Avatar asked Mar 30 '16 18:03

Stephen H. Anderson


People also ask

What is a best practice when unit testing a controller?

Controller methods should be unit tested using assert for REST HTTP Status code. Use the HttpStatusCode enum type to Assert the responses. Assert. IsType<OkObjectResult>(result);

What is unit test in asp net core?

Unit testing involves testing a part of an application in isolation from its infrastructure and dependencies. When you unit test controller logic, only the content of a single action or method is tested, not the behavior of its dependencies or of the framework itself.


2 Answers

As the fundamentals theorem says:

We can solve any problem by introducing an extra level of indirection.

You don't really need a wrapper, all you need is to avoid DateTimeOffset.Now/DateTimeOffset.UtcNow.

Here are a few ways you could handle that:

  • If you use dependency injection, write an IClock interface which exposes the Now/UtcNow properties.

    public interface IClock
    {
        DateTimeOffset Now { get; }
        DateTimeOffset UtcNow { get; }
    }
    
    internal class Clock : IClock
    {
        public DateTimeOffset Now => DateTimeOffset.Now;
        public DateTimeOffset UtcNow => DateTimeOffset.UtcNow;
    }
    

    In your tests, you just mock the interface as you wish.

  • If you'd rather keep using a static property, write a static type, let's say Clock, and use that.

    public static class Clock
    {
        internal static Func<DateTimeOffset> DateTimeOffsetProvider { get; set; }
            = () => DateTimeOffset.Now;
    
        public static DateTimeOffset Now => DateTimeOffsetProvider();
        public static DateTimeOffset UtcNow => DateTimeOffsetProvider().ToUniversalTime();
    }
    

    In your tests, you can substitute DateTimeOffsetProvider.

    Here's a .NET 2 version:

    public static class Clock
    {
        internal delegate DateTimeOffset DateTimeOffsetProviderDelegate();
        internal static DateTimeOffsetProviderDelegate DateTimeOffsetProvider { get; set; }
    
        public static DateTimeOffset Now { get { return DateTimeOffsetProvider(); } }
        public static DateTimeOffset UtcNow { get { return DateTimeOffsetProvider().ToUniversalTime(); } }
    
        static Clock()
        {
            DateTimeOffsetProvider = delegate() { return DateTimeOffset.Now; };
        }
    }
    
like image 64
Lucas Trzesniewski Avatar answered Oct 13 '22 00:10

Lucas Trzesniewski


As you won't know the value of DateTimeOffSet.Now, then all you can't assert that DateTimeOffSet.Now equals a value.

You probably should refactor to use one of two methods:

  • Dependency Injection
  • Interface and Wrappers

Dependency Injection (DI)

DI means instead of having the method determine the date, you pass it in.

This method . . .

public void DoSomething()
{
   var now = DateTimeOffSet.Now;
   // Do other stuff with the date
}

. . . would change to this method

public void DoSomething(DateTimeOffSet dtos)
{
   // Do other stuff with the date
}

Interface and Wrapper

Your other option (although in the end you would use DI with this too) is to create an Interface and a Wrapper. Then use you the interface in your object instead of a concrete DateTimeOffSet, so you can then use MOQ or other testing library to moq the interface. Check out SystemWrapper (https://github.com/jozefizso/SystemWrapper) project for an example.

like image 32
Rhyous Avatar answered Oct 12 '22 23:10

Rhyous