DEV Community

Marcell Cruz
Marcell Cruz

Posted on

Test everything with jest.

This post is a guide on how to quickly setup jest for any type of javascript application, and start testing your code right now 😄

Contents:

  • How to setup for node.
  • How to setup for es6.
  • How to setup for typescript.
  • Unit Testing fundamentals.
  • Testing DOM interactions.
  • Testing with snapshots.

First of all, Why and when should you start writing tests, this is a controversial topic, but I have to talk about it really quickly.

Some people are test advocates and they'll tell you that you should write tests for everything, and all sorts of tests(unit, integration, acceptance, etc...) and some people don't like tests at all they say it's a waste of resources, hard to manage, and in a lot of situations don't ensure that the code is working as it should anyway.
I believe that the truth is in the middle and you should stop and think about what you're doing and decide which tests you should write and how many, there's no good answer that will work for everybody, this tutorial will only cover unit tests, which are the easiest to set up and also the most critical ones in most situations.

How to setup for node.

Example repo: Jest Node

This is the easiest one, just install jest

npm i --save-dev jest
Enter fullscreen mode Exit fullscreen mode

Add the test script to package.json or change it if is already in there.

in package.json

...
"main": "calc.js",
  "scripts": {
    "test": "jest --watch"
  },
  "keywords": [],
...
Enter fullscreen mode Exit fullscreen mode

Create a test folder in the root of the project, where you'll place your tests, usually, people name it //_tests//_ but you can name it any way you like. Name your tests matching the name of the file that you want to test, you can have a look at the example repo, but basically, if you want to test a file called calc.js. you should create a test file called calc.test.js, jest by default will search any file that has ".test." in it and run the tests.

and that's it 😄
If you just want to test node application you can jump to "Test Fundamentals" otherwise keep reading.

How to set up for es6.

Example repo: Jest Node

Very similar to the node setup.
Install jest

npm i --save-dev jest
Enter fullscreen mode Exit fullscreen mode

Add the test script to package.json or change it if it's already in there.

in package.json

...
"main": "calc.js",
  "scripts": {
    "test": "jest --watch"
  },
  "keywords": [],
Enter fullscreen mode Exit fullscreen mode

Create the folder //_tests//_ and put your tests there with .test.js as the extension, have a look at the Exemplo repo if something goes wrong

Additionally, you need to configure babel to compile your tests.
inside "./babel.config.json" or "./.babelrc" depending on which version of babel you're using, add this, or change the env key if your configuration already has it.

..
"env": {
    "test": {
      "plugins": ["@babel/plugin-transform-modules-commonjs"]
    }
  }
...
Enter fullscreen mode Exit fullscreen mode

Install the plugin

npm i --save-dev "@babel/plugin-transform-modules-commonjs"
Enter fullscreen mode Exit fullscreen mode

Again if you have any trouble have a look at the example repo.
and that's it 😄
If you just want to test an application with es6, you can jump to "Test Fundamentals" otherwise keep reading.

How to set up for typescript.

It's very similar to the es6 setup.
Install jest, jest types definition, babel, babel-typescript, and typescript.

npm i --save-dev jest @types/jest typescript @babel/preset-env @babel/preset-typescript typescript
Enter fullscreen mode Exit fullscreen mode

Add a file called babel.config.json or edit the one if it already exists

{
  "presets": [
    [
      "@babel/env",
      {
        "targets": {
          "edge": "17",
          "firefox": "60",
          "chrome": "67",
          "safari": "11.1"
        },
        "useBuiltIns": "usage",
        "corejs": "3.6.4"
      }
    ],
    "@babel/preset-typescript"
  ],
  "env": {
    "test": {
      "plugins": ["@babel/plugin-transform-modules-commonjs"]
    }
  }
}
Enter fullscreen mode Exit fullscreen mode

Add the test script to package.json or change it if it's already in there.

"scripts": {
   ...
   "test": "jest --watch"
   ...
 }
Enter fullscreen mode Exit fullscreen mode

Add a folder called __tests__ with your tests in it, the file needs to have a .test.js extension so jest can find the files, if you have any trouble with the setup have a look at the example directory in exemplo repo

Unit Testing Fundamentals

To make your code easier to test you need to understand some fundamentals about unit testing.
There're two ways of testing something.

  • Return value of the function being called.
  • Side effect of the function being called. The ideal way of testing a function is the first one. not only that but side-effects are bad for testing and also bad for you code quality overall and you should strive to have a codebase with as few non-pure functions as possible, so then what is a pure function, or a function without side-effects.

Pure functions

A pure function is a function where the return value is only determined by its input values
e.g:

const sum = (x, y) => {
  return x + y;
}
Enter fullscreen mode Exit fullscreen mode

The more pure functions you use in your code the easier it will be to understand the code, compose functionality, test, and refactor, actually it's better not to refactor if you don't have pure functions, but that's a topic for another post.
So what's a non-pure function then...

Non-pure function

let x = 3;
const sum = (y) => {
  return x + y;
}
Enter fullscreen mode Exit fullscreen mode

As you can see the value of the return statement gonna change depending on the state of the app, in this case, the variable x, so sometimes you would call sum(1) and the return value would be 4 and sometimes the return value is gonna be something different if x was changed by another part of the app. avoid writing code like this as much as you can.

The second way of unit testing your code is by the side effect created by the function that you're testing, DOM interaction is a good example.

Testing DOM interactions.

Example Repo
DOM interactions are not pure.
When you click a button and this button changes the structure of the page, you've created a side-effect.
e.g

const $ = require('jquery');
const fetchCurrentUser = require('./fetchCurrentUser.js');

$('#button').click(() => {
  fetchCurrentUser(user => {
    const loggedText = 'Logged ' + (user.loggedIn ? 'In' : 'Out');
    $('#username').text(user.fullName + ' - ' + loggedText);
  });
});
Enter fullscreen mode Exit fullscreen mode

In this case, when you click the button the HTML tag with the user name is changed, the function that you're testing, the click function, didn't return anything for you to compare, so the only way to test something like this is comparing the side-effect with the desired output.

in the test file...

$('#button').click();
expect($('#username').text()).toEqual('Johnny Cash - Logged In');
Enter fullscreen mode Exit fullscreen mode

You can have a look at the example repo to see the complete working example, which also has mocks, another concept used in tests.

Testing with snapshots

Sometimes you need to test something that returns or produces a side-effect too complex to reliably compare, or that changes with considerable frequency
e.g

test('test function with huge json return value', () => {
  expect(getJSON()).toEqual(//...huge json here..//)
});
Enter fullscreen mode Exit fullscreen mode

in this case, you don't want to copy a lot of JSON to your tests making it cumbersome to test it, and making your code hard to read.
That's when snapshots come to the rescue.
Instead of writing the whole returned JSON by hand or saving in a different file, jest can do this for you, you just need to use the snapshot feature.

test('test function with huge json return value', () => {
  expect(getJSON()).toMatchSnapshot();
});
Enter fullscreen mode Exit fullscreen mode

What jest is going to do in this case is to save what getJSON returns as the value to be compared, so you need to make sure that the value returned by getJSON is correct before you can save the snapshot by running the test.
After the snapshot is saved, the value is going to be compared with the return value of getJSON every time you run the tests.
If you need to change the function and test it again, you can update the snapshot values through watch mode in your terminal by pressing "w" and then "u" or you can run

// In your terminal
jest --updateSnapshot
Enter fullscreen mode Exit fullscreen mode

and that's pretty much it for testing with jest, there's a lot about testing that I didn't cover in this post. but with this initial knowledge, you can start writing tests with any flavor of javascript that you use and can expand your codebase to use some frameworks like react and vue and also write tests for those in a similar manner.

Remember tests is not about being perfect, having some test is better than having no tests and now you don't have any excuse to start a project without any tests 😄

Top comments (0)