The normal solution is to hide it behind the 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)) {
In unit test, you can use a 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);
I do not want to introduce another interface in my class to get the current time. This is too difficult a solution for such a small problem. The solution is to use a static class with a public function.
public static class SystemTime { public static Func<DateTime> Now = () => DateTime.Now; }
Now we can remove the ISystemTime injection, and the RecordService looks like
public class RecordService { public void RouteRecord(Record record) { if (record.Created < SystemTime.Now.AddMonths(-2)) {
In unit tests, we can just as easily make fun of system time.
[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);
Of course, there is a flaw in all of this. You use public fields (HORROR!), So no one stops you from writing such code.
public class RecordService { public void RouteRecord(Record record) { SystemTime.Now = () => DateTime.Now.AddYears(10); } }
I also think that it’s better to train developers than to create abstractions to protect them from any errors. Other possible problems are related to testing. If you forget to restore the function back to its original state, this may affect other tests. It depends on how the unit test runner runs the tests. You can use the same logic to perform file system operations
public static class FileSystem { public static Action<string, string> MoveFile = File.Move; }
In my opinion, the implementation of such functions (from time to time, simple operations with the file system) using public functions is quite acceptable. This simplifies code reading, reduces dependency, and can easily be a mockery in unit tests.