And you shouldn't use it.
By that I mean, you shouldn't use it directly.
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):
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.
Signup for my newsletter here.
Visit Us: thinkster.io | Facebook: @gothinkster | Twitter: @gothinkster
Top comments (10)
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.
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. :)
Huh. I partially agree on the topic, I guess. Partially, because I believe that it's alright to use semantic names for the functions. It's kinda “clean code” and Uncle Bob must be happy about this way.
But I mostly agree w/ this topic because Date object is broken. It's easy to understand the point when you see how many people use libraries such as Moment, Luxon, Date FNS, etc. Most of the time you're not satisfied of the built in functional of Date object.
Actually, in my team we've even built our own tiny wrapper for Date API:
funbox / chronos
One library to rule the time
but does everything you need
Intlunder the hood.
When we started to develop our projects, we picked the most popular date library that existed back then We had used only two methods it provided, but got all the bundled ones, with all the possible locales.
OK, we set…
But is it testable?
What do you mean? The repo has tests, if this what you're talking about.
Also there's no problem with E2E or Unit tests in our projects.
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.
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.