Testing NodeJs/Express API with Jest and Supertest
Chinedu Orie

Chinedu Orie @nedsoft

About: Software Engineer, passionate about knowledge sharing.

Location:
United Kingdom
Joined:
Apr 27, 2018

Testing NodeJs/Express API with Jest and Supertest

Publish Date: Aug 26 '19
369 33

This is part three of building API using Express, Sequelize, and Postgres. In part two, we built simple API endpoints to demonstrate CRUD operations in Sequelize. In this article, we'll be focusing on writing end-to-end tests for the API endpoints created in part two.

Explanation of terms

  • End-to-end testing - a type of test which tests that the flow of an application from start to finish works as expected. This is also known as functional testing. An example of this type of test is testing an endpoint or a route which involves testing everything needed for the endpoint to work such as database connection, dependencies, etc.

  • Test Runner - a library or tool which picks up source code (tests) in a given directory or file, executes the test and write the result to the console or any specified location, example Jest, Mocha.

  • Jest - Jest is a JavaScript testing framework developed by Facebook. It works out of the box with minimal configuration and has in-built test runner, assertion library and mocking support.

  • Supertest - A library for testing Node.js HTTP servers. It enables us to programmatically send HTTP requests such as GET, POST, PATCH, PUT, DELETE to HTTP servers and get results.

Now that we have explained the basic terms, let us dive deep into the main business.
If you've been following along from the previous articles, then open it in your favorite text editor, else clone the repository used here.

Step 1 - Install Jest and supertest

Open your terminal and cd to the project root directory and run the command below:



npm install --save-dev jest supertest


Enter fullscreen mode Exit fullscreen mode

Step 2 - Configure Jest

Open the package.json and add the code below to it.



 "jest": {
    "testEnvironment": "node",
    "coveragePathIgnorePatterns": [
      "/node_modules/"
    ]
  },


Enter fullscreen mode Exit fullscreen mode

That is the basic configuration that we need to get jest set for testing our API. Any file that you want jest to ignore is placed inside the "coveragePathIgnorePatterns". "coveragePathIgnorePatterns" specifies a regex that matches the directory to be excluded, in our case we want it to ignore node_modules directories.

Next up we add the test script. Inside the scripts portion of the package.json, add the script below:



"test": "jest"


Enter fullscreen mode Exit fullscreen mode

Step 3 - Test Jest configuration

Now, let us confirm that jest is all set to run our test. In the terminal run npm test. You will notice an error as shown below printed on the console, this is an indication that jest is set up.

Jest error when no test is specified

Let us add a simple test to verify the configuration. Create a new directory called tests and add a new file sample.test.js. Inside the sample.test.js, add the code below:



describe('Sample Test', () => {
  it('should test that true === true', () => {
    expect(true).toBe(true)
  })
})


Enter fullscreen mode Exit fullscreen mode

Now, run npm test, you'd get an output as shown below:
Sample test output

How does Jest recognize a test file?

Jest recognizes test file in three ways:

  • files that have extension .test.js
  • files that have extension .spec.js
  • All files inside __tests__ folder or directory.

Testing the API Endpoints

Now that we have got the test environment set up, it is time to start testing the API endpoints. Since our endpoint needs to make a request to the database, we need to set up a test database. The reason for setting up a test database is that we'll be dropping the database each time we run a test. Dropping the database each time the test is run ensures the integrity of the test. That is, if a test is about creating a post record in the database, we want to be sure that there was no post record in the database before the test was run, that way, we are sure of the result got from the test.

Step 4 - Create a test database
In the part one of this article, we created two databases, one for development and the other for testing. Follow the link to create a test database if you have not done so.

Step 5 - Configure test scripts

We need the following scripts:

  • pretest - The pretest is an npm script that is automatically invoked when the npm test command is invoked. We'll hook in the command to change the environment to test and refresh the database before each test runs.

  • migrate:reset: This command will be responsible for refreshing the database before each test runs.

Now edit the scripts for package.json as shown below:



"scripts": {
    "start-dev": "nodemon index.js",
    "migrate": "npx sequelize-cli db:migrate",
    "migrate:reset": "npx sequelize-cli db:migrate:undo:all && npm run migrate",
     "test": "cross-env NODE_ENV=test jest --testTimeout=10000",
    "pretest": "cross-env NODE_ENV=test npm run migrate:reset"
  }


Enter fullscreen mode Exit fullscreen mode

What to note from the modification of the script:

  • cross-env - an operating system agnostic package for setting environment variables. We used it to set the NODE_ENV to test so that our test can use the test database. Run the command below to install cross-env.


npm i -D cross-env


Enter fullscreen mode Exit fullscreen mode
  • --testTimeout flag - This increases the default timeout of Jest which is 5000ms. This is important since the test runner needs to refresh the database before running the test.

Step 6 - Test the scripts



npm test


Enter fullscreen mode Exit fullscreen mode

If everything is okay, you should see the output below on the terminal:

Alt Text
Looking closely at the screenshot above, you'd notice a line using environment "test" which shows that cross-env has changed the NODE_ENV.

Final Step - testing the routes/endpoints

Now, let's begin writing tests for the endpoints. Create a file named routes.test.js inside the tests directory



touch tests/routes.test.js


Enter fullscreen mode Exit fullscreen mode
  • Testing Create Post Endpoint

Copy the following code into tests/routes.test.js:



const request = require('supertest')
const app = require('../server')
describe('Post Endpoints', () => {
  it('should create a new post', async () => {
    const res = await request(app)
      .post('/api/posts')
      .send({
        userId: 1,
        title: 'test is cool',
      })
    expect(res.statusCode).toEqual(201)
    expect(res.body).toHaveProperty('post')
  })
})


Enter fullscreen mode Exit fullscreen mode
  • The describe function is used for grouping together related tests
  • The it is an alias of test function which runs the actual test.
  • The expect function tests a value using a set of matcher functions.

visit the Jest docs for a full list and details of jest functions.

Now, run the test



npm test


Enter fullscreen mode Exit fullscreen mode

The output is shown below:

Alt Text

For the complete code for the tests for all the endpoints, check the repository.

Conclusion

We have been able to walk through the process of writing tests for API endpoints that interacts with the database. In the last part of this article, I'll be writing on integrating CI/CD and code coverage tools to the test environment. Until then stay tuned.

Feel free to reach out to me if you have any questions or suggestions on making the article better. You can also share your thoughts via the comment section below. Thanks!

This article was originally published in my blog

Comments 33 total

  • avinash8686
    avinash8686Sep 20, 2019

    There are no cases for api failure, It would be great if u add the failure cases as well, overall the post was pretty good and easy to understand.

    • Chinedu Orie
      Chinedu OrieSep 22, 2019

      Yeah, that's true, I seemed to focus on the success side. I'll update it to take into account your feedback. Thank you.

    • James Robert Perih
      James Robert PerihOct 28, 2019

      You could:

      expect(res.statusCode).toEqual(500);
      
      Enter fullscreen mode Exit fullscreen mode

      when you do something intentionally wrong, as well as:

      expect(res.body).toEqual('Post not found');
      
      Enter fullscreen mode Exit fullscreen mode

      Various other uses of expect here

      • Chinedu Orie
        Chinedu OrieOct 29, 2019

        Yeah, I've included some failure test cases in the sample project, I'd update the article to reflect it. Thanks for the hints.

  • Yasser Nascimento
    Yasser NascimentoNov 21, 2019

    Nice! Thanks a lot :)

  • Kedar Paritkar
    Kedar ParitkarNov 27, 2019

    set content-type text/plain not working in supertest. every time it converts the text to object. any idea why ?

  • King Elisha
    King ElishaDec 13, 2019

    Great article

  • shubhu2812
    shubhu2812Mar 8, 2020

    In this case how can I get codecoverage?

  • Martin Nuc
    Martin NucApr 17, 2020

    It looks like the database is not refreshed between endpoint tests, right? Means endpoint tests could affect each other, right?

    • Chinedu Orie
      Chinedu OrieApr 18, 2020

      Yes, that is definitely a short-cut solution, you could make each test suite independent of each other

  • Chigbogu Orji
    Chigbogu OrjiApr 21, 2020

    I tried using jest after I first read your article, but as I changed from Windows7 to Windows10 after Microsoft stopped its update(windows7); my jest test ain't working anymore.

    I got the error app.address is not a function
    What could be the cause, am using yarn as my package manager.

    I have being using mocha and chai for my tests and it works, none works these days
    Thanks in anticipation.

  • anjneeksharma
    anjneeksharmaApr 21, 2020

    i did the same setup but i am getting bellow error.
    "npm ERR! missing script: migrate:reset"

    • Chinedu Orie
      Chinedu OrieApr 21, 2020

      The error said that a script is missing? do you have the migrate:reset script in package.json?

  • mememe
    mememeJul 1, 2020

    This is good content. I hope you get to complete the whole CRUD testing.

  • akopchinskiy
    akopchinskiyAug 7, 2020

    What about more real-world complex example with database connectors and sharing them around several test suites with Jest? )))

  • Swapnil Deshaware
    Swapnil DeshawareAug 15, 2020

    How do we make it exit after completing the test case executions, apologies if this isn't the right place to ask such query, but it'd be helpful

    • Swapnil Deshaware
      Swapnil DeshawareAug 15, 2020

      Nevermind I got it, we must use --forceExit. Thank you for the post. It helped me today :) Cheers to you!!!

  • Isaac Komezusenge
    Isaac KomezusengeOct 1, 2020

    Good article with rich content!! Thank you Orie

  • Prashant Singh
    Prashant SinghJan 7, 2021

    Thanks for the article, I followed the instructions and wrote the test code. For anyone who is looking for the sample Express Route Test using Jest (As explained in the above article) can visit this sample GitHub Repo I created.
    Repo Link: github.com/prashant1k99/JWT-example

  • Adilson Carvalho
    Adilson CarvalhoMar 21, 2021

    Hello Chinedu,

    It's 2021 and your post still helping people. I am one of them.

    Cheers mate!

  • Kirk Douglas Jr
    Kirk Douglas JrJun 14, 2021

    Thanks

  • Warren Wong
    Warren WongJun 25, 2021

    The two links to how to setup database are erroring out.

  • Hidayah Ramadlana (ID)
    Hidayah Ramadlana (ID) Jul 22, 2021

    your website oriechinedu.com/ is not accessible

  • Gennady77
    Gennady77Oct 5, 2021

    Links in the Step4 doesn't work

  • Gabriel Basilio Brito
    Gabriel Basilio BritoFeb 6, 2022

    Step 2 is not needed anymore. Is the default behavior. Except the test script of course

  • Emma F. Mkpurunchi
    Emma F. MkpurunchiNov 23, 2022

    Nice and enriching article.

  • Bruno Peixoto
    Bruno PeixotoNov 30, 2022

    I receive a timeout with log ": Timeout - Async callback was not invoked within the 10000ms timeout specified by jest.setTimeout.Timeout - Async callback was not invoked within the 10000ms timeout specified by jest.setTimeout.Error:
    "

  • Zainab-Saad
    Zainab-SaadJul 19, 2023

    Thanks for this great tutorial, the links in this post are not working, could you please update the post?
    It would be great help :)

  • Kartick Sadhu
    Kartick SadhuDec 24, 2023

    After configureing jest when i run sample test code i got this error
    Image description

  • Abiola Abdulsalam
    Abiola AbdulsalamAug 7, 2025

    for those having trouble accessing his websibe, use this Link instead: chineduorie.com

Add comment