- [Instructor] We've tested callbacks with Jest,
but that's not the only kind of asynchronous code.
What about promises?
Jest supports promises natively, which is a great feature.
To test a promise, just return the promise
and put the assertion in the .then or .catch.
You do not need to use the callback named done.
If you're catching rejections,
then you should also use expect.assertions
with the number of assertions to expect
to specify how many assertions there were supposed to be.
If you don't, then a fulfilled promise would pass.
There's two additional chaining matchers
that can simplify writing tests.
The resolves matcher
will cause Jest to wait for the promise to resolve.
If the promise is rejected, then the test fails.
The rejects matcher works in a similar way.
If a promise is fulfilled, then the test fails.
When using the rejects matcher,
you don't have to specify the number of assertions.
We'll use this syntax in the upcoming challenge
at the end of the chapter.
Of note: When testing promises, do not use a callback
because if you give a promise to done,
the test will automatically fail
as done throws if it receives a value.
Other unexpected behavior can occur
depending on how the tests were written,
including timeouts if a promise isn't returned
and unhandled rejections
which can be difficult to troubleshoot.
I compiled a sandbox test suite of examples
of how not to test promises.
In the sandbox folder and the 03_08 subfolder,
run npm install, then npm test to see all the failures.
Explore promises.test.js to see the source code.
Let's write some correct tests,
but what should we be testing?
Let's switch over to VS Code.
From the Explorer, open up lib, then reservations.js.
This file is the library of operations
on multiple reservations.
All the functions return promises,
but the first three use the database.
If we scroll down to validate, we can see
that it's just calling the validator with a callback
and returning a promise.
Perfect.
Our candidate for the first promise unit test
is the validate function for a couple of reasons.
It returns a promise,
it uses the already-tested validator,
it does not interact with a service,
and it has a consistent signature and structure.
Let's write some tests in VS Code.
Right-click on lib in the Explorer and go to New File.
Name it reservations.test.js
and press Enter.
First, we'll need the reservations library,
which is what we'll be testing.
So const reservations
equals require,
and then we're going to require
from the relative directory, reservations.
We're also going to need the reservation schema
in order to create the instance to test.
So const reservation equals require,
and then in the schema directory, we want reservation.
Now that our requires are complete,
let's create the validate suite.
Add a new line, then describe validate.
These tests are going to be very similar to the validator test
except there's no named callback.
So it should resolve
with no optional fields,
and then an arrow function expression,
give it a reservation.
So const reservation equals new Reservation,
and then give it an object, so a date of 2017/06/10,
a time of 06:02 am,
a party of four,
a name of family,
and an email of username@example.com,
trailing comma and a semicolon.
We're going to return reservations.validate
with the instance of the reservation that we created.
This returns a promise.
On the next line, .then, take the value,
and then I expect the value .toEqual
the reservation that we passed into it.
Add a semicolon and then a semicolon and semicolon.
That's all for the first one.
What about a rejection?
It should reject
with an invalid email,
and then empty arrow function expression,
then copy and paste the reservation from above,
change the email to just username,
and then add a new line,
then we're going to manually specify the number of assertions
as it's good to see the full version before the shortcut.
So expect.assertions
parenthesis one semicolon.
Add a new line,
then return reservations.validate reservation.
This returns a promise.
And this time we're going to catch an error,
and I expect error
.toBeInstanceOf
the class Error with a capital E.
That's all we need.
Make sure everything is closed out, and save.
Open up a terminal,
and then let's run our tests, npm test.
Great, everything passed.
We're well on our way to improving our codebase.
Let's clean up here and close out our test files,
close out the terminal, close this,
and then open up README.md.
Last but not least, let's update the documentation.
Above debugging, let's add a new heading, testing.
This project uses Jest for testing.
Visit https://jestjs.io
for details.
Include some simple usage instructions.
To execute tests,
npm test, surrounded by back ticks.
Save the changes.
It's best practice to inform other developers
about what's available.
Close out any remaining open windows.
In this chapter,
we've been busy validating correctness with unit testing.
We started with a survey and comparison of features
of various Node.js testing frameworks.
Then we installed the core selection, Jest.
We discussed what and where to unit test,
including some different file organizations.
We wrote some unit tests,
then tested asynchronous callbacks and promises.
However, there is more code to be tested
like functions that depend on the database.
If pure unit testing requires isolation,
how do we pretend that code is executed?