Check out a free preview of the full Enterprise UI Development: Testing & Code Quality course
The "Mocking Time" Lesson is part of the full, Enterprise UI Development: Testing & Code Quality course featured in this preview video. Here's what you'd learn in this lesson:
Steve uses the useFakeTimers and setSystemTime methods to mock the system time for a test. These methods freeze the system time and allow it to be set to any date-based value to make testing easier and more predictable.
Transcript from the "Mocking Time" Lesson
[00:00:00]
>> The one that I do use somewhat frequently, and this will be where we kind of see a little bit more of this in practice. And I encourage you all cuz like there are various things to play around with and we will. I would follow along to this section cuz like we'll do an exercise where I think it's interesting to us is time.
[00:00:18]
Right time you're allowed to mock right in my fictitious ruleset that is a blog post waiting to happen of Steve's rules on when you are not allowed to mock based on his opinions rather than facts. Time right and so for instance because reasons, I'll give you the real-world use case for me.
[00:00:40]
I've got these drop-downs for filtering, right? They send what looks like a SQL query to the back-end that should get you the results. The old UI that we were migrating from had this drop down with like, last 24 hours. Last three days, whatever right drop down. Originally we were just re-platforming so that we could pay down some tech debt and deploy to our own cloud.
[00:01:13]
That query engine on the back end does not support last 24 hours. That's not a thing, right? You can't send it last 24 hours, right? So but you have to send it a query, right? So the way that we do that is pretty simple, which is start time.
[00:01:31]
It's 24 hours ago, right? So take and then on elasticsearch that works on sequel lite you actually I have to put an end time that I had to put in the future. It's not important. So there were all these use cases where I'm like, hey, depending on which backend you're talking to, and this and that and the other thing, there's a lot of logic here.
[00:01:55]
So I should write some tests, right. Mostly because for a sanity check, all these different things, I wanted the feedback loop. Once I got it working, I was pretty sure no one's going to touch this code again. I don't need the test coverage for reliability, I need test coverage so I don't go insane, changing this thing and doing the mental math in my head.
[00:02:14]
So in that case, I wanted to see like, hey, if I put 24 hours ago, it should have been 24 hours ago. What's the problem with time?
>> Time Zones.
>> Well, there's lots of problems with time. In the words is it like seal as it? But like it's originally, is a Kenny Loggins time keeps on ticking, ticking, ticking into the future, all right.
[00:02:40]
You can put a value in your code base. It's instantaneously a broken test, right? And in those cases, that is when you might choose to mock out time, right? So let's take a look. I have another kind of, this was just simply a little dummy file that I could write code in and see instantaneous results.
[00:03:01]
We have this other one here called time zone, which is not about time zones, does this file pass? Cool, cuz what I don't need to do is I have a half done file and then be really confused when I like tried to do another GitHub build action later and it doesn't compile.
[00:03:15]
So we go into this time zone are we running the server we are, so, this app does a few things. It shows you date.now. It shows you how long you've been staring at this page. This is hitting some public API, right? So, it does all the things that you might actually choose to mock in an application.
[00:03:42]
We have real apps that will use like playwright but I just needed something tiny. What if we needed to test this thing this component is really hard to test all the functions in here really hard to test and it's got almost all the problems that I've been leading up to.
[00:03:56]
In one little microcosm, I will put an extra fun ones in there, shuffles them with math dot random as well. All right, same we were that you UID now I would argue to kind of like start to pull a lot of our unit testing talk together that like stuff we did with asymmetric matrix yesterday right.
[00:04:14]
Where we like there's parts of this I don't care about, right? Think about that, that's an option that I wanna leave on the table for you. But in some cases, you're like I wanna just make sure this thing mostly renders the same. Maybe you are using a snapshot for a good reason.
[00:04:30]
Math.random is gonna ruin your life, time is gonna ruin your life, and a third-party API is going to ruin your life, right? There are three problems with this ridiculous component, all of which are bad. So if we look at it, yeah, should we show the to-dos at all?
[00:04:47]
It keeps the current time and sets it on an interval. Yeah, everything that you hate in the world is happening in here. It makes an API request and then it renders it to the page. So you can imagine that like if I did something really like egregious in this case, like for instance, this one was like giving us trouble earlier.
[00:05:14]
Like should match snapshot? Well that's not gonna go well, cuz that snapshot will by definition. And this is why things were breaking before. So we can do, let's do npx vitest. It fails. Because the time is different than the last time that it rendered. Also, apparently I changed something since then, but that doesn't really matter either way.
[00:05:47]
So you could update the snapshot and it passes Until you run it again and then it fails. All right, and this is like from my like, this is what I got went through with that query builder. And this is a place where you might choose to do something like this.
[00:06:05]
So what you could do is say something like, this is why because y'all been asking you every time you're like, can I use it before each now? I might not yet, not yet. Now, for each you could have used it the entire time. I just choose not to cuz I've been hurt too many times.
[00:06:25]
Sometimes you have to be like, Steve says this is the right thing just realize like Steve's been hurt. Use fake timers, and you can guess what that other method was, use real timers. It should probably go in the after each or after all, I don't really care which one you do.
[00:06:43]
It's cool and so now this one fails. Why does it fail? Because I was back to importing this time. Okay, all right. We've got the broken snapshot. It fixes. Run it again. Let's see real quick what our issue is and because it should. Unless it behaves slightly differently let's set the time Okay, yeah, maybe it's out of date or maybe there's a slight of ingest or maybe I'm just wrong.
[00:07:39]
I was under the impression it does freeze it at the Unix epoch, but I could have been wrong. So we use fake timers and then I set the system time to whatever time I want if you want. If you hate this, you can put use new date and put in, you can set it any date you want.
[00:07:56]
I just took the one that was in my snapshot and went with it.
>> Basically, setting the system time to the fake time that you created on line six, okay.
>> Yep.
>> And could you write this yourself? Could you monkey patch date.now, store the real date.now in another function, and then restore it after the test?
[00:08:15]
You absolutely could. You shouldn't, but you absolutely could. So this is now setting the time to one single point where it makes total sense to do that. Right, and so this will freeze it in one place. There are things that you can do, for a case of a snapshot test you can probably just freeze it, right?
[00:08:40]
In the case of an animation, you might choose to move time forward, right? And you can do that you can also do a like set to a new system time, that you can add
>> You could also do like a.
>> Why?
>> Yeah?
>> Maybe I'm misunderstanding, but on line seven, instead of that number, couldn't you put line six inside of those parentheses?
[00:09:08]
Or is that line six to a variable and then put that variable in the parentheses?
>> What's the, the variable is just freezing time.
>> Yeah.
>> Yeah, line six is freezing time?
>> Yep.
>> The value actually came from the snapshot.
>> Yeah, I don't super understand what, is there extra complexity or a problem?
[00:09:28]
You said not to do this, to do the date object. I feel like I do one time
>> One, like for me, so for instance, I was like the use case that I had, I'm actually displaying this number. So great, right. The use case that I had of If I like and I chose very specific dates, I chose January 1st of that year, right?
[00:09:48]
Cause it's easy in my head to do what was one month earlier, December 1. So if you're that's the logic that you're using on the use case your app, yeah we'll put in new date right of we're doing the math, right? Any form of a representation of a date will work, right?
[00:10:07]
And so, new date also, it's can I live remember the various syntaxes you can hand to new date I'm not entirely confident that I could. We'll find out together, that was also part of it, is the fun parts of live coding. That does work so yeah, if your logic is like, let's say you're writing a function that is like.
[00:10:34]
Four hours ago on a blog post and for some reason you're not using a library for reasons I don't understand. That's not true, all those date libraries are terrible. But like you could theoretically choose to do something like this as well. And so, this will like freeze it at that as that particular date or something along those lines.
[00:10:55]
So, whenever you rerun the snapshot. It's still going to do that right there
>> Yep
>> Yea, it's set to that day so things where you need anything where you can do this for any like effective like random number right. If I do something like get to dues which is like I told you we hit an API That randomly hits, it
[00:11:17]
randomly shuffles like 5 blog posts, right? It breaks again. This one has a closing UL tag so update. Yeah, be great. Just update the snapshot. That's why you don't you snapshots, by the way. Now that one works. We can I got to check that one out and see if I broke something.
[00:11:39]
But you could also in this point, choose to stub out math.random, right? I wonder if faking the time breaks for reasons I don't understand. But you can choose to stop any of these things for things you don't control, right? The other things, the one thing you wanna make sure that you do in this case though is you wanna say after each.
[00:11:58]
What do you think you do in this case?
>> Set the time back to reality? Yeah, like you need to make sure and I'll talk about these in a second as well. But here we go. So we've got we have.
>> Clear all timers?
>> Use real timers again, so that will put everything back after every test.
[00:12:28]
Now your tests should run in isolation in the modern era.
>> A lot of these API methods are from older test suites. These days in the modern era, most of your tests will run in a separate process. And there's less likelihood that you'll leak out but like yeah it's nice to be able to switch out Jest provide tests and not have to like worry about cleaning that stuff out.
[00:12:50]
Jest will also I believe to that as well. But like I'm not sure what Mocha does for instance.
>> afterEach above the tests, is it still gonna do it after it will still do it after?
>> This is now a question of like, what do you like more.
[00:13:06]
Reading it yeah.
>> Yeah.
>> How do you like.
>> Right, this is more likely that you're not gonna mess it up. The other one feels right.
>> Yeah [LAUGH].
>> [LAUGH] Right, it doesn't matter.
>> Feels right to you [LAUGH].
>> Yeah, exactly, right, and this is why programmers are terrible people.
[00:13:20]
>> [LAUGH]
>> And I have to import after each, or things won't work. And again, there are certain things in this workshop, particularly when we talked about playwriting a second where like could some of these things deserve their own entire workshop? In fact, I think for unit testing, there is one.
[00:13:36]
For Cyprus, there is one. I did it. It's great. You should watch it. For playright, there presently isn't one. So we'd like for some of these, we're doing the high level version of this. To kind of show you the wide range of tools that you can deploy in various ways to solve common problems.
[00:13:50]
And when you can and cannot use them in a large code base. But these these will work for special things. Time I'm okay with things I don't control. I mostly harping on this because I it is a talk that I have to have with people, I work with, you know what I mean?
[00:14:04]
Like I the general thought process around this is a little bit dangerous. So this is like one of the hills I'm willing to die on. So you can start time. Like again if we wanted to, for instance, mock out like, fetch, you could do that here to get the API request.
[00:14:21]
Whatever you need to do to get this like stabilised. So again you notice that I'm not talking like actually doing the mock up of an API cuz I want to show you a better way that is kind of newish. And we will kind of you know let's transition to what I've made my point about mocks
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops