Jest
Jest is one of the most commonly used testing environments used in the javascript & typescript developer communities.
Setup
Install Jest
Install jest as a dev dependency using this command (for npm):
npm install --save-dev jest
For yarn or pnpm:
pnpm add -D jest
The default environment is often not sufficient, so next let's configure it.
Configure Jest Environment
Jest automatically looks for tests with the following pattern:
**/__tests__/*
*.test.[js|jsx|ts|tsx]
This means any .test.js
, .test.jsx
, .test.ts
, .test.tsx
file or
file within __tests__
will get executed by jest to evaluate tests.
Now this is a good default search path to use,
let's not fight it and setup a ./__tests__/
directory.
Then create a testing spec file.
mkdir __tests__
touch __tests__/some-module.test.js
First Test File
With a test file set up,
create the first test to make sure it works.
We'll assume a module some-module.js
is being tested with
the test file ./__tests__/some-module.test.js
, shown below.
// ./__tests__/some-module.js
const some-module = require('./some-module.js');
describe('some-module.addTwoNums(a, b)', () => {
it('a = 1, b = 2, returns 3', () => {
expect(some-module.addTwoNums(1, 2).toBe(3);
});
});
Jest uses a common function pattern of describe
-it
-expect
.
The describe()
function sets up a collection of individual tests.
This is a good place to put entire modules or functions of a module.
It can even be nested, maybe one for an entire module and the
nested describe
within it could be used for each funciton.
The describe
function takes a string to describe the test collection and
a function that gets called to perform the various tests.
Then the it()
function is used for an individual test.
Typically it's used to describe a single condition to test against.
In the above example a = 1
, b = 2
should result in the number 3
.
You can test many cases of this condition within it
.
Each it
should have one or more expect
statements within it.
Each expect
is a test case,
so proving additions of numbers result in the correct sum would be one
test condition represented by it
.
There's several ways to test this condition,
infinate ways in fact in this case.
To test a couple of those you could have several expect
statements.
Maybe one for a = 1, b = 2
, one for a = 1, b = -1
and a = -10, b = -5
modifying the above example like below:
// ./__tests__/some-module.js
// inside describe()
it('returns correct sum', () => {
expect(some-module.addTwoNums(1, 2)).toBe(3);
expect(some-module.addTwoNums(1, -1)).toBe(0);
expect(some-module.addTwoNums(-10, -5)).toBe(-15);
});
// ...
Now there's several test cases to test the same condition described in
the it
function by using several expect
functions.
You'll notice there's toBe(someValue)
following expect
.
This is known as a matcher.
Matchers
After accepting the return of a function within an expect
statement,
it's going to be necessary to evaluate the result to some specification.
This is where matchers come into play.
The expect
return object comes with several matcher functions.
So far the toBe()
matcher has been used and it handles scalar values.
For more details check out Jest's matcher API.
Recipes
These are specific use cases for Jest that has come up for me.
Ensuring Jest only Uses UTC Timezone
When testing using Date
objects or any time based API,
a lot of problems occur that depend on the current time zone of
the computer running the tests.
The computer doing the testing might not even be in the same timezone as
as the computers that run the code.
Not to mention problems associated w/ daylight savings times.
The best way to avoid all of this is to use one consistent timezone,
ideally UTC.
Here is how to setup jest to consistently execute tests as if the computer is in the UTC or equivalent GMT timezone. Most of these suggestions come from a stackoverflow answer on setting jest timezone to UTC.
Ensure Jest Configuration has a globalSetup
Global setup is the only lifecycle configuration that happens early enough
in the jest testing procedure to ensure a timezone is set correctly.
Do this by editing the jest config to have a script run during the
globalSetup
lifecycle step.
Substitute the path here with the file location preferred for the
global setup script.
{
"globalSetup": "./.jest/global-setup.js"
}
Set Timezone in globalSetup Script
Now within the globalSetup script,
defined here as a file in ./.jest/global-setup.js
,
and ensure the environment variable for timezone tz
is set to UTC
.
// ./.jest/global-setup.js
module.exports = async () => {
// ... other setup actions, preferably environment variables happen 1st
process.env.tz = 'UTC';
// ... other setup options, most should probably happen after env
};
(Optional) Add a Test that Ensures UTC Execution
It would be useful to ensure in any Date sensitive test file that
the node interpreter thinks the current timezone is UTC.
To do that,
in any test file or describe block where time testing is necessary,
use the below example describe-it-expect
block to ensure UTC is in use.
describe('Ensure TZ is set to UTC for this test suite', () => {
it('timeZoneOffset is 0', () => {
expect(new Date().getTimezoneOffset()).toBe(0);
});
});
This creates a timestamp of the current time.
Then it checks the timezone offset from the host nodejs timezone.
If the timezone is UTC
or GMT
it should be 0.