w3resource

Testing Web Frameworks


Jest is actually a universal testing platform, although it is often considered to be a React-specific test runner. Jest has the ability to adapt to any JavaScript library or framework. In this tutorial we will teach you how to integrate Jest into other popular JS libraries other than React.

Vue.js

Note: we assume that you have a Vue project already created if not please run:

npm install -g vue-cli
vue init webpack vue-test
cd vue-test

To run Jest in this Vue project you have to install some dependencies:

npm i -D jest vue-jest babel-jest

You need jest-vue-preprocessor to make Jest understand your .vue files, and you need babel-jest for the integration with Babel.

Also you will need to install vue-test-utils:

npm i -D vue-test-utils

Once you are done with installing the dependencies, you have to add the following configuration in the package.json

"jest": {
  "moduleNameMapper": {
    "^vue$": "vue/dist/vue.common.js"
  },
  "moduleFileExtensions": [
    "js",
    "vue"
  ],
  "transform": {
    "^.+\\.js$": "<rootDir>/node_modules/babel-jest",
    ".*\\.(vue)$": "/node_modules/vue-jest"
  }
}

The moduleFileExtensions tells Jest which extensions to look for, and transform tells Jewhich preprocessor to use for a file extension.

Finally, you have to add a test script to the package.json:

{
  "scripts": {
    "test": "jest",
    ...
  },
  ...
}

Testing a Component

Let us consider a test for a single file Component (a file that contains its html, css and js in one component).

To do this, you need to first create a ChatList.vue component in src/components:

<template>
    <ul>
        <li v-for="chat in chats">
            {{ chat }}
        </li>
    </ul>
</template>

<script>
export default {
  name: 'list',
  props: ['chats']
}
</script>

Then you need to update your App.Vue as shown below:

<template>
  <div id="app">
    <ChatList :chats="chats"/>
  </div>
</template>

<script>
import ChatList from './components/ChatList'

export default {
  name: 'app',
  data: () => ({ messages: ['Hey John', 'Howdy Paco'] }),
  components: {
    ChatList
  }
}
</script>

So far we have created a couple of components that we can use for our testing. Next we will create a folder under the project root, and an App.test.js file:

import Vue from "vue";
import App from "../src/App";

describe("App.test.js", () => {
  let cmp, vm;

  beforeEach(() => {
    cmp = Vue.extend(App); // Create a copy of the original component
    vm = new cmp({
      data: {
        // Replaces data value with this fake data
        chats: ["Aryan"]
      }
    }).$mount(); // Instances and mounts the component
  });

  it('equals chats to ["Aryan"]', () => {
    expect(vm.chats).toEqual(["Aryan"]);
  });
});

Now when we run npm test, the test runs and passes. In the case where you are modifying tests you have to run the above command in watch mode:

npm test -- --watch

Redux

Follow this steps to write tests for a Redux code.

Setup

Install jest and babel-jest:

npm install --save-dev jest```
```npm install --save-dev babel-jest

Then you configure your code to use babel-preset-env features in .babelrc:

{
  "presets": ["@babel/preset-env"]
}

You should add this to the scripts in your package.json

{
  ...
  "scripts": {                                                                                                                                       
    ...
    "test": "jest",
    "test:watch": "npm test -- --watch"
  },
  ...
}

Finally, you can run npm test to run it once, or you can run npm run test:watch to test every file change.

Express.js

Follow the steps below to find out how to test an Express with Jest.

1. Install

The first thing that you need to do is to install babel-cli, babel-preset-env, jest, supertest and superagent.

npm install --save-dev babel-cli babel-preset-env jest supertest superagent

2. Separate your app and sever

This is so that Jest won't be listening to your port after testing

//app.js
const express = require('express')
const app = express()

app.get('/', (req, res) => {
    res.status(200).send('Hello World!')
})

module.exports = app
//server.js
const app = require('./app')

app.listen(5678, () => {
    console.log('Your Example app listening on port 5678!');
})

3. Use done to notify that it endsM

Jest test ends when it hits the last line of the test function, so you will need to use a done() to make it right.

const request = require('supertest');
const app = require('../../src/app')

describe('It should test the root path', () => {
    test('It has to response the GET method', (done) => {
        request(app).get('/').then((response) => {
            expect(response.statusCode).toBe(200);
            done();
        });
    });
});

4. Promise way

const request = require('supertest');
const app = require('../../src/app')

describe('Test the root path', () => {
    test('It should response the GET method', () => {
        return request(app).get("/").then(response => {
            expect(response.statusCode).toBe(200)
        })
    });
})

5. Async, await way to test

const request = require('supertest');
const app = require('../../src/app')

describe('It should test the root path', () => {
    test('It has to response the GET method', async () => {
        const response = await request(app).get('/');
        expect(response.statusCode).toBe(200);
    });
})

6. Using Supertest

You can also use the supertest way. All you need to do is to return the statement and remember not use .end() and the end.

A working example is as shown below:

const request = require('supertest');
const app = require('../../src/app')

describe('It should test the root path', () => {
    test('It has to response the GET method', () => {
        return request(app).get('/').expect(200);
    });
})

7. Database connection

You have to handle database connection during the tests, this is done using this pattern:

describe('It has to test the addLike method', () => {
    beforeAll(() => {
        mongoDB.connect();
    });```

    afterAll((done) => {
        mongoDB.disconnect(done);
    });
}

Here we used mongoDB. We will connect at the beginning of this test suite, and disconnect at the end. And the mongoDB is just a wrapper class.

You can consider using beforeEach and afterEach to manage the database connection so as to prevent race condition.

module.exports = {
    mongoose,
    connect: () => {
        mongoose.Promise = Promise;
        mongoose.connect(config.database[process.env.NODE_ENV]);
    },
    disconnect: (done) => {
        mongoose.disconnect(done);
    },
};

Notice that done? It is a variable used to indicate that an async operation is finished. Jest will give that as a parameter for that afterAll.

8. Running the Tests

You may want to use jest --runInBand to run the tests depending on how you structure your tests. Otherwise, multiple tests access same collection causes random failing for your tests. In order not to use -runInBand, you can open and close database connection with beforeEach and afterEach instead of beforeAll and afterAll.

GatsbyJS

To run jest in Gatsby follow the steps below:

1. Install the dependencies

npm install --save-dev jest babel-jest react-test-renderer babel-preset-gatsby identity-obj-proxy

2. Create a Jest configuration file

jest.config.js
module.exports = {
  transform: {
    "^.+\\.jsx?$": `<rootDir>/jest-preprocess.js`,
  },
  moduleNameMapper: {
    ".+\\.(css|styl|less|sass|scss)$": `identity-obj-proxy`,
    ".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": `<rootDir>/__mocks__/file-mock.js`,
  },
  testPathIgnorePatterns: [`node_modules`, `.cache`, `public`],
  transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`],
  globals: {
    __PATH_PREFIX__: ``,
  },
  testURL: `http://localhost`,
  setupFiles: [`<rootDir>/loadershim.js`],
}

3. Use mocks to complete your testing environment

Mocking the gatsby module is recommended. Though it is not needed at first, however, it will make things easier when you want to test components that makes use of Link or GraphQL

__mocks__/gatsby.js
```const React = require("react")
const gatsby = jest.requireActual("gatsby")

module.exports = {
  ...gatsby,
  graphql: jest.fn(),
  Link: jest.fn().mockImplementation(
    // these props are invalid for an `a` tag
    ({
      activeClassName,
      activeStyle,
      getProps,
      innerRef,
      partiallyActive,
      ref,
      replace,
      to,
      ...rest
    }) =>
      React.createElement("a", {
        ...rest,
        href: to,
      })
  ),
  StaticQuery: jest.fn(),
  useStaticQuery: jest.fn(),
}

4. Write the test

Create a test file and add it to a __tests__ directory, or you put them elsewhere, it should have the extension .spec.js or.test.js. for instance:

src/components/__tests__/header.js
```import React from "react"
import renderer from "react-test-renderer"

import Header from "../header"

describe("Header", () => {
  it("renders correctly", () => {
    const tree = renderer
      .create(<Header siteTitle="Default Starter" />)
      .toJSON()
    expect(tree).toMatchSnapshot()
  })
})

5. Running the tests

Inside your package.json there is already a script for test, this currently outputs an error message. Change this to use the jest executable that we now have available, like so:

"scripts": {
    "test": "jest"
  }

Previous: Troubleshooting
Next: Jest Expect