You cannot completely neglect one essential area of software development: automated testing. There are many different testing libraries or test runners available for JavaScript. However, today we'll learn about Jest, a testing library developed and used internally by Facebook, that resembles Mocha the previous king of JavaScript testing libraries.
Installing Jest
Jest works well for testing backends and it shines when it comes to testing React applications. Since tests are only executed during development, we will dive in and understand Jest from scratch by (1) creating a new directory, (2) running an npm project and (3) installing Jest as a development dependency with the command:
$ mkdir understandingJest
$ npm init -y
$ npm install --save-dev jest
Setting Jest
Even if the default testing environment in Jest is Node, it's best to specify it by easily adding the following to the end of the package.json file:
{
"jest": {
"testEnvironment": "node"
}
}
If you are building a web app, you can use a browser-like environment through jsdom
instead.
The jest
command line runner has a number of useful options. So, to make our lives easier, we will define the npm script test
to execute and report Jest tests with --verbose
and --runInBand
.
{
"scripts": {
"test": "jest --verbose --runInBand"
}
}
--verbose
displays individual test results with the test suite hierarchy (more on that later with describe blocks) and --runInBand
runs all tests serially in the current process, rather than creating a worker pool of child processes than run tests, which can be useful for debugging.
Directory Structure for Testing
To adhere to Node.js best practices, create two folders named: (1) test and (2) utils. Inside test create a understandingJest.test.js file and inside utils create a helper_functions.js file. Last but not least, at the root directory let's create a db.json file.
Your project should have the following directory structure:
├── node_modules
├── test
│ └── understandingJest.test.js
├── utils
│ └── helper_functions.js
├── db.json
├── package-lock.json
├── package.json
Helper Functions and Mock Data
Let's create some simple functions in our helper_functions.js file for sum
, average
, and biggest
and export them to use for our automated testing:
const sum = (a, b) => {
return a + b
}
const average = array => {
const reducer = (sum, item) => {
return sum + item
}
return array.reduce(reducer, 0) / array.length
}
const biggest = array => {
const reducer = (biggest, value) =>{
return biggest > value ? biggest : value
}
return array.reduce(reducer, 0)
}
module.exports = {
sum,
average,
largest
}
In our db.json file we are going to mock a JSON database for laptops. To keep things SS, we will only have a single item as follows:
{
"laptops": [
{
"name": "Macbook Pro 13",
"price": {
"amount": 1299,
"currency": "USD"
},
"display": "Retina display",
"onlyHasUSBTypeC": true,
"hasTouchBar": true,
"builtInApps": [
"Siri",
"Safari",
"Messages",
"Facetime"
]
}
]
}
Now let's do some Testing!
In Jest, individual test cases are defined with the test
function. The first function parameter is the test description as a string. The second parameter is the testing function, that defines the functionality for the test case.
The test first executes the code to be tested, meaning that we run the testing functions and then it verifies the results with the expect function. The expect
function gives you access to a number of 'matchers' that let you validate a ton of different things.
For now, let's take a look at a single test using our understandingJest.test.js file:
const hf = require('../utils/helper_functions')
test('...sum value', () => {
const result = hf.sum(5, 5)
console.log(`The sum result is: ${result}`)
expect(result).toEqual(10)
})
In the example above, we are bringing the helper function in hf
to use inside our testing function as hf.sum
to (1) add two values, (2) console log the result and (3) check if that result equals 10 as set arbitrarily. Remember, the 'matchers' can be set at will depending on our test case.
At this stage, we can run this sum test by using the following command:
$ npm run test
and we should get the following results in our terminal:
> understandingjest@1.0.0 test
> jest --verbose --runInBand
PASS tests/understandingJest.test.js
✓ ...sum value (18 ms)
console.log
The sum result is: 10
at Object.<anonymous> (tests/understandingJest.test.js:6:13)
Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 0.483 s, estimated 1 s
Ran all test suites.
Collection Blocks and more Testing
Describe blocks can be used for grouping tests into logical collections. The test output of Jest also uses the name of the described block. This time around, let's write some test to use the rest of our helper functions.
const hf = require('../utils/helper_functions')
describe('What is the', () => {
test('...sum value', () => {
const result = hf.sum(5, 5)
expect(result).toEqual(10)
})
test('...average value', () => {
const result = hf.average([5, 5, 5, 5, 5])
expect(result).toEqual(5)
})
test('...biggest value', () => {
const result = hf.biggest([1, 15, 3, 2, 4])
expect(result).toEqual(15)
})
})
Running tests one by one
The npm test
command executes all of the tests of the application. When writing tests, it's usually best to execute only one or two tests at once. To run a single test (or describe block) we can specify the name of the test with the -t flag:
$npm test -- -t 'name-of-spec'
Alternatively, if you have multiple testing files you can also choose to only run the tests found in a particular file
$npm test -- tests/understandingJest.test.js
Running tests from a database
To run backend tests using Jest, you can query databases using SuperTest and SuperAgent in an API-like fashion. Today we will go easier on us, and use the db.json we created earlier and explore the .toHaveProperty(keyPath, value?)
matcher with the following tests:
const db = require('../db.json')
const macbookPro = db.laptops.find(laptop => laptop.name === "Macbook Pro 13")
describe('The latest Macbook Pro is', () => {
test('...very expensive', () => {
expect(macbookPro.price.currency).toBe('USD')
expect(macbookPro.price.amount).toBeGreaterThan(1000)
})
test('...probably the only computer with NO normal ports', () => {
expect(macbookPro.onlyHasUSBTypeC).toBe(true)
})
test('...at least some builtInApps for free', () => {
expect(macbookPro).toHaveProperty('builtInApps', ["Siri", "Safari", "Messages", "Facetime"])
})
})
In the example above, we first start by getting the database from the db.json file. Then we find the object we want to test by using the Array.prototype.find()
method. Finally we use our selected object and run tests for find out if our computer is (1) very expensive, (2) if it doesn't have normal ports, and (3) if it was the four built-in apps we need.
Test results would look as follows:
> understandingjest@1.0.0 test
> jest --verbose --runInBand
PASS tests/understandingJest.test.js
The latest Macbook Pro is
✓ ...very expensive (2 ms)
✓ ...probably the only computer with normal ports
✓ ...at least some builtInApps for free (1 ms)
Test Suites: 1 passed, 1 total
Tests: 3 passed, 3 total
Snapshots: 0 total
Time: 0.501 s, estimated 1 s
Ran all test suites.
Feel free to look at the repository containing all the project files for this article here.
So off we go, hopefully, with a better understanding about Jest testing for applications. Thank you for reading and happy testing!
Top comments (0)