The normal solution is to hide it behind interface.
public class RecordService
{
private readonly ISystemTime systemTime;
public RecordService(ISystemTime systemTime)
{
this.systemTime = systemTime;
}
public void RouteRecord(Record record)
{
if (record.Created <
systemTime.CurrentTime().AddMonths(-2))
{
// process old record
}
// process the record
}
}
In the unit test you can use mock object and decide what to return
[TestClass]
public class When_old_record_is_processed
{
[TestMethod]
public void Then_it_is_moved_into_old_records_folder()
{
var systemTime = A.Fake<ISystemTime>();
A.CallTo( () => system.Time.CurrentTime())
.Returns(DateTime.Now.AddYears(-1));
var record = new Record(DateTime.Now);
var service = new RecordService(systemTime);
service.RouteRecord(record);
// Asserts...
}
}
I don’t like to inject another interface into my class just to get the current time. It feels too heavy solution for a such a small problem. The solution is to use static class with public function. This solution is nothing new and you can find same example from way back 2008 from Ayende's blog.
public static class SystemTime
{
public static Func<DateTime> Now = () => DateTime.Now;
}
Now we can remove the ISystemTime injection and RecordService looks like this
public class RecordService
{
public void RouteRecord(Record record)
{
if (record.Created < SystemTime.Now.AddMonths(-2))
{
// process old record
}
// process the record
}
}
In the unit tests we can mock the system time just as easily.
[TestClass]
public class When_old_record_is_processed
{
[TestMethod]
public void Then_it_is_moved_into_old_records_folder()
{
SystemTime.Now = () => DateTime.Now.AddYears(-1);
var record = new Record(DateTime.Now);
var service = new RecordService();
service.RouteRecord(record);
// Asserts...
}
}
Of course there is a downside to all this. You are using public fields (The HORROR!) so nobody is stopping you writing code like this.
public class RecordService
{
public void RouteRecord(Record record)
{
SystemTime.Now = () => DateTime.Now.AddYears(10);
}
}
But why would you do something like that? Not even junior developer is likely to make such a mistake. Also I think it is better to educate developers than create abstractions just to protect them from doing any mistakes. Other possible issues are related to running the tests. If you forget to restore the function back to it's original state it might affect other tests. This depends on the way unit test runner executes the tests.
You can use the same logic to mock file system operations
public static class FileSystem
{
public static Action<string, string> MoveFile = File.Move;
}
In my opinion implementing this kind of functionality (mocking time, simple file system operations) using public functions is perfectly acceptable. It makes the code easier to read, decreases the dependencies and it is easy to mock in the unit tests.