- [Instructor] Jest provides a mechanism
known as mock functions for controlling code execution.
Jest mock functions can do a few things
that are useful for testing codes.
They can change the implementation of a function
if the original isn't necessary for a test.
Mocks can capture calls and parameters
for a function for instrumentation.
Finally, they can set the return values of a function
including throwing, resolving, and rejecting.
Jest has three ways of mocking functions and modules.
The first foundational method is jest.fn,
which mocks a single function.
The next is jest.mock, which mocks an entire module.
The last is jest.spyOn, which takes an object
and a method name.
Similar to mock, it mocks the entire module,
but it also spies on the given method,
Each builds upon the previous,
so we'll start with FN.
I'll cover the others later in the chapter.
Jest.fn creates a new empty mock function.
By default, a mock function returns undefined.
This can be useful if you want to just prevent something
from working and keep track of how it was used.
.fn accepts an optional argument known as an implementation.
Practically, an implementation is just a function
which returns an arbitrary result,
including values, thrown exceptions, and so forth.
Every Jest mock function includes a special .mock property
regardless of whether the mock function was created
with or without an argument.
What's inside?
The .mock property has four properties of its own
that keep track of its usage in a series of arrays.
Calls keeps track of how it's been called.
Each call includes the arguments that were used
when it was called or just undefined.
The instances property keeps track of the actual instances
created by the function, if any.
invocationCallOrder keeps track of the incrementing order
in which the mocks were called,
which is useful for determining
if something was called before or after something else.
Finally, results keeps track of both the type
and value of what was returned.
Before we mock some functions,
let's see some practical examples.
You can follow along with the slides, or you can take a look
at sandbox/04_02 to see the code.
Creating an empty mock is straightforward.
Just call jest.fn and assign it to a variable.
I'm going to assign the variable actual to the result
of invoking the mock function.
Since we didn't provide an implementation,
it will return undefined.
Therefore, we can expect a comparison
of the actual to undefined to be true.
There's a nicer way writing this.
The matcher toBeUndefined is more explicit.
Of course, implementations can be used
to control return values.
We can start with an expected constant with a value.
Similar to the empty mock,
we can assign mock function to jest.fn,
but this time, we pass in an argument of a function
that returns expected.
We can then assign actual to be the result
of the mock function.
This time we can expect that actual is the same as expected.
There's another syntax for specifying implementations,
which is useful for defining the default implementation
of a mock function that's created from another module.
Same as before, set expected to value.
This time, we'll just start with an empty mock.
Then we can use the method mock implementation
to set the return value to expected.
Assign actual to the mock function invocation
and actual will be the same as expected.
Jest mocks keep track of the calls
with a mock.calls property.
Let's start with an empty mock function.
The mock calls property is an array,
which by default has a length of zero.
There's a matcher to check the number of calls,
toHaveBeenCalledTimes,
which if it hasn't been called will be zero.
Let's invoke the function.
Now, we expect that the calls length will be one.
We can also check with a matcher toHaveBeenCalled,
which takes no arguments of note.
There isn't the opposite, which is why we've used
toHaveBeenCalledTimes with a value of zero.
Calls keep track of how it was called
in an array of arguments, which we can use for comparison.
It's kind of unwieldy.
So the toHaveBeenCalledWith matcher is a way more
readable way of using it and doesn't require wrapping.
Jest has the ability to keep track of invocation call order.
To see it, create two different mocks using jest.fn,
then invoke each of the mocks.
We can then expect
that the first mocks invocationCallOrder
was less than the second mocks invocationCallOrder.
This is kind of awkward and there's no matcher within Jest
for comparing invocationCallOrder.
The third party module jest extended does though,
with toHaveBeenCalledBefore.
Finally, Jest mocks can track instantiations
within the instances property.
We'll start with a mock function.
Instead of mocking it, we'll create an empty instance
and a second instance that we're arbitrarily passing
a value to.
Now for the comparisons,
we can expect that instance empty is the first element
of the instances array as it was created first.
In a similar way, we can expect
that instance value is the second element.
Since there were two instances,
we can expect that the instances length is two.
Let's review the foundational jest method FN.
FN creates a mock function,
which returns undefined by default.
You can pass an optional implementation
or use mock implementation to control return values.
Each mock function includes the .mock property,
which includes four arrays.
Calls, including the arguments used when it was called.
Instances keeps the actual instances
created by the function.
invocationCallOrder keeps track of the order
in which mocks were called.
And finally, results keeps track of both the type
and value of what was returned.
That's enough theory.
Let's use a Jest mock function.