JavaScript has a nice Date Object. It can do all kinds of cool things like tell you the current date and time, etc. Lots of languages have a similar facility.
And you shouldn't use it.
By that I mean, you shouldn't use it directly.
Let me clarify a little bit. I don't mean don't use date objects, those are a fundamental data type in JavaScript (or your preferred language). But what I mean is that you shouldn't directly use the functionality that tells you the current date and time, or some calculation based on current date/time, such as getting tomorrow's date. Why?
Because the date and time is an interaction with an exterior system. Because of how languages incorporate them into basic functionality, we have a hard time seeing that, but you should view it the same way you view the HTTP call that charges a credit card. Would you access the XMLHTTPRequest object directly from your code and hardcode in the URL to charge a credit card? No. You abstract that into its own functionality.
There are two reasons we do this. The first is that the code is complex and we want to extract it to a simpler interface. That really doesn't apply with getting a date & time. But the second reason does apply. And that reason is that extracting the functionality to somewhere else allows us to test and build our system rapidly by creating fake situations.
Let's examine a simple application that sends you an email on Wednesday evening at 8 pm to remind you to take the garbage out. How can you test this functionality to assert that it's working? Wait a whole week until Wednesday evening at 8 pm? Definitely not. Would you modify the system date/time on your development machine? Please for the love of all that is good and holy and bug-free don't do that. Maybe you can make the trigger date/time configurable, and when testing, keep setting it to 1 minute from now. Even that isn't the best idea. To test you'll have to keep changing settings in your development data store.
Instead, imagine you coded your system something like this (I'll use a bit of pseudocode):
Ignore my abstracted pseudocode to make this more readable (JavaScript dates are terribly unreadable). The key here is the "myDateClass" object. I wouldn't actually name it that but you get the point. This isn't the Date() function. This is something else. This class wraps the Date() object/function. The same way that you'd wrap the native XHR object, or any other functionality that accesses an external system. That way you can easily test your system by providing an alternative implementation that makes "now" exactly what you want it to be.
There are other ways to make this work. You can treat the IsTimeToSendEmail as the abstraction that you fake. You don't need to wrap all the functionality of your built-in Date object. But whatever you do, use an abstraction.
This is the proper way to use the current Date/Time functionality in a system.
Most developers don't automatically see the current date/time as an external system, but it is. So abstract accessing it away in its own function/class/service/whatever makes your code easier to write, read, and maintain.
Happy Coding!
Signup for my newsletter here.
Visit Us: thinkster.io | Facebook: @gothinkster | Twitter: @gothinkster
Top comments (8)
What is wrong with creating a date object with a specific date/time for your unit test?
Yes, let's wrap this API in an API that our API can use.
Also, I do not use XMLHTTPRequest because no sane person would want to bootstrap all that every time they needed to do a request, it's a crap interface. :)
hence why we used to go out of our way to abstract it away.
Sorry, if I seem aggressive, maybe I need to get some sleep.
But this just seems so wrong to me in JS context. :)
I'm not crazy about this solution. You wrote a whole wrapper class and a fake for that wrapper class, all of which you have to maintain, when you could just
This solution takes on less technical debt and is also functionally pure and referentially transparent. You know that every time the function is called for a given date object, it will always return the same result, which helps A LOT with debugging.
(Not that there aren't any valid reasons for writing a date wrapper--I've written them before for different reasons.)
Very good. Great alternative solution. Ultimately my solution and your solution are very similar. Two ways to accomplish the same goal.
On a different point, "this solution takes on technical debt". I see no evidence of either solution containing any "technical debt". Cutting a corner is technical debt. Debt is something that usually needs to be paid back. These techniques (like what you showed here) are about improving the quality of code, which if it has any effect on technical debt, is a reduction.
Good point. I was really unclear in my earlier comment. Perhaps "technical debt" isn't exactly the right phrase. When I said "technical debt", I was referring to the additional maintenance cost of the wrapper and the fake(s) and the cost of the additional developer time it takes to understand the codebase fully. Maybe it's not exactly "technical debt" like a hacky workaround that you know you'll have to eventually "pay back" by fully undoing. More like a technical utility bill or a technical property tax--a long term cost that is a lot smaller than a loan but that you don't ever really get rid of.
Definitely agree on that.
I made something similar in the past. I used the JS Date object as a dependency, wrapped in a service which was the only point able to handle directly the Date object.
Also consider that there are a lot of libraries to handle JS and this approach lets you change to one of those pretty easily if you want to.
sorry, it sounded aggressive. Didn't mean it to be.
I meant exactly that. do unit tests work well?
And very cool job. Well done with putting that together.
But is it testable?