I have business logic that does not perform certain functions on Saturday or Sunday. I want my unit tests to verify that these functions are performed, but the tests will f
You should create an abstraction over the system clock, as you should create abstractions over other system resources and external resources to be able to write RTM unit tests.
An abstraction over the system clock is pretty simple and could look like this:
public interface ISystemClock
{
DateTime Now { get; }
}
Classes in your application that depend on the system clock should than typically have an ISystemClock
as constructor argument. For unit testing scenario's you can use an ISystemClock
implementation that looks like this:
public class FakeSystemClock : ISystemClock
{
// Note the setter.
public DateTime Now { get; set; }
}
Now you can use this fake clock in your unit tests like this:
[TestMethod]
[ExpectedException(typeof(InvalidOperationException),
"Service should throw an exception when called on saturday.")]
public void DoSomething_WhenCalledOnSaturday_ThrowsException()
{
// Arrange
var saturday = new DateTime(2011, 1, 1);
Assert.AreEqual(saturday.DayOfWeek, DayOfWeek.Saturday,
"Test setup fail");
var clock = new FakeSystemClock() { Now = saturday };
var service = new Service(clock);
// Act
service.DoSomething();
}
In your application project you can define an ISystemClock
implementation that actually uses the system clock. Using the given ISystemClock
definition, it will look like this:
public class RealSystemClock : ISystemClock
{
public DateTime Now => DateTime.Now;
}
When you use a DI container, you can configure it to wire up your types and automatically inject instances of RealSystemClock
when a constructor specifies a ISystemClock
argument.
btw. While you could use Func
delegates to do the trick, defining an interface for this is much more explicit and much more readable to other developers. Besides that, wiring everything in a DI container would be much easier, because it's so easy to end up with multiple Func
dependencies that all do different things than "give me the current local time".
I hope this helps.