Express is the most popular Node.js framework for building web applications, especially REST APIs. And in this article, I'm going to show you how you can test your API endpoints with a cool library called SuperTest.

SuperTest is an HTTP assertions library that allows you to test your Node.js HTTP servers. It is built on top of SuperAgent library, wich is an HTTP client for Node.js.

Getting Started

Let's start by creating a simple Express server with a single endpoint that returns a simple json.

index.js
const express = require("express")
const app = express()

app.get("/", (req, res) => {
  res.send({ name: "John Doe" })
})

app.listen(8000, () => {
  console.log("Server has started!")
})

To test it manually, you can simply run node index.js and send a request to http://localhost:8000 with Postman or cURL.

$ curl http://localhost:8000
{"name":"John Doe"}

To make our code testable, we need to separate our listen method to another file, so that we can require our express instance without starting the server.

index.js
const server = require("./server.js")

server.listen(8000, () => {
  console.log("Server has started!")
})
server.js
const express = require("express")

const app = express()

app.get("/", (req, res) => {
  res.send({ name: "John Doe" })
})

module.exports = app

We have our server up and running, now it's time to write some test.

SuperTest Basic Usage

Before we get started with SuperTest, we need to install a testing framework. It's a handy tool to write automated tests, so that you'll know what part of your application went wrong.

In this tutorial, we will going to use Jest. Which is by far the easiest testing framework for JavaScript I've ever used.

$ npm install --save-dev jest

Then, we need to setup our test command by adding a test script inside our package.json.

package.json
{
  // ...
  "scripts": {
    "test": "jest"
  }
  // ...
}

After Jest installed and configured, we now can write our first test by creating new test file.

server.test.js
const app = require("./server")

test("GET /", done => {
  supertest(app)
    .get("/")
    .expect(200, JSON.stringify({ name: "John Doe" }))
    .end(done)
})

Here we require our server.js file to get our Express.js server instance. Then, create a new test called "GET /", run a GET request to / endpoint and expect the result to be the defined object. We also want to make sure that the response has 200 HTTP status, which means that our request is OK.

If you notice, when we're asserting our response, we are stringifying our JSON object. That's because by default, supertest will compare our response as a string.

We can now run our tests by running npm test or npm run test.

Using Callbacks

There are other approach to assert your server response. Instead of passing the expected result as an argument, we can pass a callback to get and assert our response.

supertest(app)
  .get("/")
  .expect(response => {
    expect(response.status).toBe(200)
    expect(response.body).toEqual({ name: "John Doe" })
    done()
  })

By using the callback approach, we're asserting your response body and status code directly inside our response callback. We also need to run Jest done function to finish our test when our assertion is completed.

We also get our response body as a JavaScript Object, so we can compare it directly with toEqual method provided by Jest matchers.

Send Form Data

Form input is the most essential feature of dynamic web applications nowadays. And testing a endpoint with form data is a piece of cake in SuperTest.

To handle form data, we need to install another third-party library called Body Parser. Body Parser is an Express middleware that we can use to handle form data inside our Express app.

If you don't know what middlware is, essentially, it's just a function that can intercept users request. In this can we use it to get our form data.

We can install Body Parser by running the command below.

$ npm install body-parser

Then, we can use Body Parser middleware inside our server.js file.

server.js
const express = require("express")
const bodyParser = require("body-parser")
const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
// ...

We can now access users form data inside our route handler by accessing req.body variable.

server.js
// ...

app.post("/form-data", (req, res) => {
  const formData = {    name: req.body.name,    age: req.body.age,  }
  res.send(formData)
})

// ...

To test it out, we can send our form data by calling the field method for every field in our form inside our supertest request.

supertest(app)
  .get("/form-data")
  .field("name", "John Doe")
  .field("age", "25")
  .expect(response => {
    expect(response.status).toBe(200)
    expect(response.body).toEqual({ name: "John Doe", age: "24" })
    done()
  })

JSON Request Body

By default, supertest will send your form data with Content-Type of application/x-www-form-urlencoded. If the client wants to send a more complex data types, you way want to use JSON format, which is application/json. To do that we can use another method from supertest called send.

send method lets you send a request with body that has a Content-Type of application/json. So that you can send a more complex data like numbers, arrays, nested objects, etc.

const formData = {
  name: "John Doe",
  age: 25,
  fruits: ["Apple", "Orange"],
}

supertest(app)
  .get("/form-data")
  .send(formData)  .expect(response => {
    expect(response.status).toBe(200)
    expect(response.body).toEqual(formData)
    done()
  })

Then, you need to add another express middleware from body-parser package, which lets you decode request body with the Content-Type of application/json.

server.js
const express = require("express")
const bodyParser = require("body-parser")

const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
// ...

File Upload

We also can upload files to our server with SuperTest!

To do this, we need to setup our server first, so that we can handle file upload from the client. The easiest way to handle file upload in Express is by using another third-party library called Multer. So, lets install it first!

$ npm install multer

Then, we can initialize a new Multer object and specify our upload directory.

server.js
const express = require("express")
const multer = require("multer")
const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
const upload = multer({ dest: "uploads/" })

// ...

Finally, we can handle the file upload by adding the Multer middleware from our Multer instance. This let us upload the file inside avatar field in our client side form.

server.js
// ...

app.post("/upload", upload.single("avatar"), (req, res) => {
  // req.file is the `avatar` file
  // req.body will hold the text fields, if there were any
})

// ...

We can test it out by using the attach method where we can specify the field name and the file path we want to upload.

supertest(app)
  .get("/")
  .field("name", "John Doe")
  .attach("avatar", "/path/to/file.png")  .expect(response => {
    expect(response.status).toBe(200)
    done()
  })

By default, our form data will be sent with Content-Type of multipart/form-data.