- We are going to write our first jest mock
for our application,
but it's going to take a little more work
than just creating a new mock function.
In lib reservations.js,
there's a function named validate,
which validates a reservation.
We've already written two tests
which test the validation library,
so there's nothing additional
that we need to test for now.
However, create calls validate
before it persists the record.
while validate doesn't access any external resources,
it's best practice to unit test in isolation.
Remember, we're testing create, not validate
and we want our unit tests to be as atomic as possible.
Therefore, we'll mock validate
when we write a create test.
We haven't gotten to the stage
of being able to mock calls to the database
so we can't pass validation.
Therefore, let's mock validate
so it arbitrarily returns a promise rejection.
In order to do that with a jest mock function,
we will have to manually back up the validate function
then replace it with our mock.
Then we can execute create and check our expectations.
Finally, we can restore the validate function
to its original state.
Why should we bother backing up and restoring?
Jest does not automatically backup
or restore a mocked function
when using .fn.
If we skipped backing up and restoring it,
we would alter the usage of validate
for the rest of our tests in the same file,
which could lead to chaos.
With that said, backing up
and restoring the original functions is not required
if all the tests in a file use the same mock.
Ready to get started?
Switch over to VS code.
In the explorer, navigate to lib
and open reservations.test.js.
Scroll to the end of the file
then add a new line
and describe the test grouping for create.
We'll use it should reject
if validation fails.
Since create returns a promise,
let's use async with our arrow function expression.
We're going to start off with a comment
where we're going to store the original.
Then const original = reservations.validate.
Then we're going to create the error
that we're going to arbitrarily reject with.
So const error = new Error
with a capital E
and then the message will just be fail.
Add a trailing semi-colon.
Now we can mock the function
by assigning reservations.validate
to be just .fn
and then we'll set the implementation
to a function that is a promise.reject
that is the error that we created.
Add a trailing semi-colon at the end
and that's complete.
Now that we've prepared the mock,
we can execute reservations create.
So we're going to await
and then expect reservations.create,
and then we'll execute it
and we're going to expect that it rejects with a plural
and then use the matcher to be,
and then the expected will be the error that we created.
Add a trailing semi-colon.
Let's also check to see
that the validation function was only called once.
So we're going to do expect reservations.validate
and then we're going to use the matcher
to be called times parenthesis
and the expected number is one.
Add a trailing semi-colon
and then finally let's restore the original function
so we don't break any other tests.
Reservations.validate = original.
Add a trailing semi-colon semi-colon semi-colon
and then save the file.
Nice work.
Let's give it a test.
Open up the terminal
and then type mpm test and press enter.
We've got a type error.
Cannot read property validate of undefined.
That's not expected.
Let's take a look and see why.
In the explorer, select reservations.js.
If we look at the create function,
it returns validate.reservation.
However, if we scroll down to the bottom of the file,
we're using module.exports.validate
and this is the thing that is being overridden
with our mock.
Therefore, in order to avoid this problem
with the require cache,
we're going to have to change the syntax
to use module.exports.validate.
This will use the mocked version.
Save the changes
and then rerun the tests.
Type mpm test and press enter.
Excellent.
We're up to seven passing tests.
Manually backing up and restoring that function
wasn't a lot of fun.
Is there a faster way?
And what if we want to mock an entire module?