Javascript Testing with Jest

2.18.2022


Unit tests and test driven development are great ways to improve the reliability and scalability of applications. You can reduce the number of bugs and be confident your code is doing what it's supposed to do. As I mentioned in my last post I have been writing a full stack javascript application. I wanted to take things a step further and add testing to this app.


Jest is a javascript testing framework, backed by facebook, that offers a wide range of testing capabilities. You can create tests for the functions within your application and automate the running via Node and NPM.

I wanted to write one test suite with a number of tests for my main application file. This client side JS file contains all the front end javascript code and multiple functions. Jest expects that you export the functions to a test file, so I ended up with this:

taken from: json.app on github

if (typeof exports !== 'undefined') {

exports.financial = financial;
exports.tester = tester;
exports.showAmount = showAmount;
exports.getPortfolio = getPortfolio;
exports.buildPortfolio = buildPortfolio;
}
else {
$(document).ready(loadApp);
}

We are exporting the functions I want to test, but only if exports are available because otherwise the file will not publish to a web browser.Within our test file we can now require the functions for testing. I wrote a few different kinds of tests to show examples of how to test different types of functions:

Expect a function with a given parameter will be equal to taken from: json.app.test on github

// testing the financial function
test('converts decimal into 2 digit floating point number', () => {
  expect(parseFloat(functions.financial(123.456))).toBe(123.46);
});

This is the most basic form of a test, ensuring that the output of a function matches our expected result. Here we use a common financial function to round to 2 decimal points so a simple test rounding this number will pass.

Many of the functions in my application involved editing and updating the DOM. The way to test for DOM updates is to create a test DOM and test against that for the expected results. I needed to include a setup file for the jsdom package and set global variables for Jquery to make these tests work: setup-jest.js script Otherwise Jest will not be able to run these kinds of tests since Jquery ($) is undefined and a DOM has not been created for the tests. Now we can do something like this in a test file:

// testing the showAmount function
test('shows user balance ', () => {

  // Set up our document body

  document.body.innerHTML =
    '<div>' +
    '  <section class="portfolio-output"/>' +
    '  <section />' +
    '</div>';

// mock showAmount with a value, should update the DOM
jest.mock(functions.showAmount(3122));
expect($('.portfolio-output').text()).toEqual('3122');
});

We are creating a dom that matches what our HTML file will look like. Then after mock calling a function that updates the dom (showAmount), we can check the DOM to see if the results match what we expected.

Another common type of function in my application is asynchronous functions. Any time you need to make a call to the server or an API this is the kind of function you will have. The way to test asynchronous functions is to create a mock function that returns what the API or Server would, then you can test the client side code you are trying to validate.

it('works with promises', () => {

  // Set up our document body
  document.body.innerHTML =
    '<div>' +
    '  <section class="portfolio-output"/>' +
    '  <section />' +
    '</div>';

  jest.mock(functions.buildPortfolio(testPortfolioObject));
  expect.assertions(2);
  return mockFunctions.getPortfolio().then(data => expect(data).toEqual([{"shares": 164, "symbol": "MSFT"}, {"shares": 48, "symbol": "AMZN"}, {"shares": 22, "symbol": "TSLA"}]))
  .then(expect($('.portfolio-output').text()).toEqual('xyz'));

});

Here we are testing an asycnhronous function getPortfolio. In the application it makes a GET request to the server and returns a json object. Here we are calling to mockFunctions.getPortfolio, which is an abbreviated mocked version of the function. All it does is return a Promise that resolves to what we would expect to receive from the Server. Since these asynchronous tests work with promises you can chain calls to them like I have done using .then() so you can call your asynchronous functions and use .then() chaining to write as many assertions as you want.