- In this section, we're going to take a closer look
at how to mock timers,
a couple of useful techniques,
which allow you to artificially move the timeline,
when you are setting up fake timers.
So, imagine you set up a fake timer.
You said just use fake timers.
That means when you call set timeout
or clear time out or set interval or clear interval
just won't call the real functions.
It'll effectively call mock functions instead,
but that's okay.
But you can call this function as well
which artificially advances all timers
as if they'd completed.
So if you'd set a timeout
in your code of a minute, when you call this function
it'll simulate the elapsed time of a minute.
It'll say, okay, the time has elapsed
and that any callbacks, which you, you were hoping to call
you know, after a minute will be invoked
but without actually having to wait for that time.
So it automatically moves the timeline to
complete any timers which are currently pending.
Closely related and more powerful,
you can advance timers by a certain number of milliseconds.
So imagine you set up a call back to happen
after 10 seconds and another call back to happen
after 20 seconds and another call back after 30 seconds.
You can artificially move the timeline by 10 seconds
like moving the timeline
in a video when you're watching it on TV.
So you can add artificially advance the timer by 10 seconds
and see if the first callback has been invoked.
And then you can artificially move the timer to 20 seconds
and see if the second call back has been invoked
without actually having to wait for 10 seconds
to it is just like fast forwarding, in a movie.
And you can control how far you want to move the timeline
and see what happens when you do that.
So we're gonna have a look at examples of both
of these functions in this section.
If you wanna have a look for yourself,
mock in timers techniques as the folder, here it is.
If you open that in the code editor,
this is what it looks like.
So I have an operation.
This is actually the same operation.
As in the previous section, I have a countdown function.
When I call it, I pass in a callback function.
It'll invoke that function after the specified number
of seconds in, in real implementation, it sets the time out.
Obviously I'm gonna to fake that time out.
So it doesn't actually have to, I don't actually
have to wait that long, but in the real world, it would wait
that many seconds.
And then eventually call me back with a message.
You know, four seconds countdown complete in a real code.
It would take four seconds.
For example, for that to come back.
Because I'm impatient,
and I can't be bothered to wait four seconds.
I'm going to fake timers, have a look.
There's my code.
This is the scenario that we're going to,
we're going to use.
I'm going to, in my test, I'm going to call that function
specify a call back and a delay.
I'll verify
that the call back function hasn't been invoked yet.
Obviously when, when you first call countdown
it hasn't invoked the call back yet.
In fact, it never would
because there isn't really a timer anymore.
So what I can do is I can artificially run the timer
to completion as if to say
well, a real timer would be waiting four seconds.
I'm going to artificially run the timer
to completion as if the timer had actually completed
but without time to wait for it.
And then I can say, well, you know
after that time after that's happened
then this call back should have been invoked.
So having artificially moved the timeline forward I can
to completion, I can then say
has the call back function now been invoked?
Okay. So you can move time without time to wait for it.
It's great.
Makes the test run more quickly.
So here's my test.
This is the first example where I'm going to run all timers.
First of all, establish an artificial fake timer.
I don't want to call the real set timeout
that would take too long.
I'm impatient, unit tests are meant to be quick.
So let's fake the timers.
What I then do is I, I call and countdown.
I specify a dummy callback.
I'm not really interested in the callback function itself.
So I've got a dummy callback function
a mock function and a timer before.
Okay. So there's a set timeout call a call
set timeout with a parameter of four seconds.
The set timeout function has been mocked.
So initially my call back should not have been called.
Okay then.
And here's the key point.
I ran all timers to completion.
In other words, I say to my, you know, faked set timer
you know, you thought you were waiting for seconds
it's finished.
So at that point, my countdown function should
invoke the call back, okay.
Having kind of moved the clock forward so
that the timer has finished after that's happened.
My countdown function should have invoked the
the call back once.
So this gives us the benefit of not having to wait
for the timer to actually complete.
We can artificially tell it, pretend that you've completed
and then see what happens afterwards.
When I run that test, it works.
And the other key point is that it didn't actually
take four seconds.
It completed effectively, immediately.
I think the timer, so set timer, wasn't really being called
and then basically simulated the completion of all timeout.
And after that point, I can verify my callback function
should have been invoked after the time it was complete.
So semantically, it behaves as if the timeout had actually
finished, and my callback function is called,
but without having to actually wait
for that to happen in real time.
Good, isn't it.
I like this.
You can take it a step further.
And instead of running all timers to completion immediately
you can move the timeline in intervals.
So have a look at this.
First of all, I'm using fake timers.
Avoid real timers.
I'm actually gonna call countdown three times.
I'm gonna give it three callback functions
three dummy callback functions.
The first dummy should be invoked after 10 seconds
of elapsed time, either real or invented the second callback
after 20 seconds and the third callback after 30 seconds.
Now there isn't any actual timeout involved
because I faked it.
I have to artificially move the timeline forward.
Before I've done any artificial movement of time,
those callbacks have not yet been invoked.
I then artificially move the timeline forward
as if 10 seconds had elapsed.
You specified milliseconds here, 10,000 milliseconds
all being well after 10 seconds of elapsed time
this first callback should have been invoked.
So has it been, let's verify
that the first callback has not been invoked
because effectively 10 seconds have elapsed.
The other callbacks should not have been invoked yet
because this callback was waiting for 20 seconds
and this callback was waiting for 30 seconds.
And so far I've just moved the clock forward by 10 seconds.
So the first function would've been called back
but the other se-
the other two haven't yet been called back.
I've actually got a full implementation.
If I show you the full code in the code editor for this.
Let's have a look,
operation with fake timers dot test dot JS.
So as you can see, I've set up my three dummy callbacks.
Okay. And then I say, you know, call back the first dummy
after 10 seconds, call back the second dummy call back
after 20 and the third dummy call back after 30
before time has moved on.
Basically the time is now still, still sitting at time zero,
no callbacks have yet been evoked after 10 seconds
of artificial time movement.
We're now on the ten second timer.
The first callback should have been invoked once,
the second callback hasn't been called yet
because it needed 20 seconds.
The third callback needed 30 seconds.
Let's move the clock on another 10 seconds.
So that's 20 seconds now cumulatively.
So after 20 seconds, okay, the first callback has still
been invoked 10 seconds ago in our imaginary time span.
The second callback has not been invoked
but the third callback hasn't been invoked yet
because it needed 30 seconds of a lapse time.
So you can kind of see how this works.
Every time you call advanced timer by, it moves the timer
forwards by a specified amount.
And you can see what should have happened
after that interval or that interval,
or after 30 seconds after another 10,
then all callbacks should now have been evoked
effectively 30 seconds have now relapsed.
And that means all the functions should have been evoked
after that amount of time in total.
So you get a chance to experience the, you know
the actual flow of time and what happens in time.
But without time to wait for 10 actual seconds
you can fast forward to see what should happen
at that moment in time simulated here.
So we just need to run the test.
So I've run the test and it works, hooray, and again
the key point to note is because I've
I'm using fake timers in a real code base.
It would take 30 seconds for those callbacks to complete
because I'm artificially advanced in time, like
in a blink of an eye, the test completes really quickly.
And that's what you want with unit testing.