Sunday, February 21, 2021

Playing with Jest

On the last post, I've talked about how to set up Jest on a project, but ... now what? Where should we start?

First we need to create a unit test file. Inside it we can put something with this structure:

describe('My component', () => {
  it ("should ...", () => {
    expect(1).toBe(1);
  })
})

The "describe" function says to jest that this is a test suit (or a group of tests).
The "it" functions are the tests. Inside we've got the test code. In this example we are expecting for 1 to be 1. We'll get into more useful tests in a bit.

Now when you do a npm run test, it should find 1 test suit with 1 test and pass it.
If we have multiple folders/files with tests and we just want to test 1 of them, we can add the component name, like this:

npm run test componentFolderName

If we want jest to automatically run everytime we change/save the file, we can add the --watch/--watchAll flag.

npm run test --watch

A good starting point is to make sure our component is defined. For that we'll need to import it before the describe function, and "expect" the component to be defined:

import a from "../src";

describe("Component A ", () => {
    it("should be defined", () => {
        expect(a).toBeDefined();
    });
});

 If we want something more useful, like comparing if a result from a function is what's expected, we can use:

const result = a.add(1,2);
expect(result).not.toBe(2);
expect(result).toBe(3);

Sometimes we want to check if objects have the same values and structure. For that we need to do a deep comparison like this:

const result = a.getObj();
const expected = { key: 1 };
expect(result).toEqual(expected)

We are also able to test asynchronous calls:

it("should work with promises", function () {
    expect.assertions(1); //number of assertions being returned

    const f1 = () => {
        return new Promise((resolve, reject) => {
            resolve("test Data"); // data returned by this function
        });
    };

    return f1().then(data => expect(data).toBe("test Data"));
});

//import 'regenerator-runtime/runtime'

it("works with async", async () => {
    expect.assertions(1);
    
    const f1 = () => {
        return new Promise((resolve, reject) => {
            resolve("test Data");
        });
    };

    const data = await f1();
    expect(data).toEqual("test Data");
});

In the odd case where our test method has a  setTimeout, we can usejest's faketimers to force all the timers to run, hence running all the functions to get a result, like this:

it("shouldn't need to wait for setTimeouts", () => {    
    jest.useFakeTimers();
    var result = setTimeout(() => {
        return "some value";
    }, 5000);

jest.runAllTimers();    
    expect(result).toBe("some value");

Often, we are not interested what a nested function does, but we want to make sure it is called:

 it("should call function once", () => { 
   
const fn = jest.fn();

    fn();

   
expect(fn).toHaveBeenCalled();
    expect(fn).toHaveBeenCalledTimes(1);

Finally, Jest comes with istanbul test converage, so all you need to run is:

npm run test --coverage

 

No comments:

Post a Comment