Testing NodeJs/Express API with Jest and Super test 🐧🐧
Tags: testdev, javascript, webdev, discuss
Published: 2020-12-21 22:35:30

# Table Of Contents
* [About Testing!](#chapter-1)
* [Jest and SuperTest Intro ! ](#chapter-2)
* [Who apply testing? ](#chapter-3)        
* [Conclusion](#chapter-4)

###**Testing** one of the most important skills you can known as a developer, It's something a lot of people don't teach or focus on but if you known testing,  So I had to try and figure it out here in **part(2)**. you can read more in article [ Testing part(1) in Node JS with Express using Mocha and Chai](https://dev.to/mhmdlotfy96/testing-a-rest-api-in-node-js-with-express-using-mocha-and-chai-1258) 

# About Testing! <a name="chapter-1"></a>
**Unit Testing**
Tests a single fully isolated unity of the application.

**Integration Testing**
Tests the interaction of a unit along with its dependencies. e.g. A function that calls another function meaning that the test results are also depending on the function being called within the parent function.

**End-2-End Testing**
Full flow tests. From the front-end button click to the API endpoint consuming.


# Jest and SuperTest Introduction <a name="chapter-2"></a>
### About Jest
* Jest is a wonderful testing library created by **Facebook** to help test JavaScript code, Express APIs, React components, and much more.
* What's great about Jest is it **not only has a similar syntax** to other testing/assertion libraries like **Jasmine and Chai**.
* And with Jest your tests **run in parallel** so they are executed **much faster** than other testing frameworks.

### About 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, which is an HTTP client for Node.js.

# Who apply testing ?! <a name="chapter-3"></a>

**(1) Getting Started**
So, in this article we're going to use the blog REST API I built using Express and Mongoose
 which you can fetch [Github repo here](https://github.com/mohamedlotfe/unit-testing-api-nodejs-jest)
 - To use jest globally we can install it with 

`$ npm install -g --save-dev jest supertest`
 - Once the packages are installed, we need to setup a test command in our `package.json file`

```javascript
{
	// ...
	"scripts": {
		"test": "jest"
	}
	// ...
}
```
**(2) Test preparation**
 - Writing your first test (You can make the test pass by expecting 1 === 1).
```javascript
// This passes because 1 === 1
it('Testing to see if Jest works', () => {
  expect(1).toBe(1)
})
```
**(3) Testing Endpoints**
 - Before you can test endpoints, you need to setup the server so Supertest can use it in your tests.
 - Most tutorials teach you to listen to the Express app in the server file, like this:
```javascript
const express = require('express')
const app = express()

// Middlewares...
// Routes...

app.listen(3000)
``` 
 - This **doesn’t work** because it starts listening to one port. If you try to write many test files, you will get an error **port in use**.
 - So you want to allow each test file to start a server on their own. To do this, you need to **export app without listening** to it.

```javascript
const express = require('express')
const app = express()

// Middlewares...
// Routes...

module.exports = app
```
 - For development or production purposes, you can listen to your app like normal in a different file like index.js.
```javascript
  const app = require("./server");
    app.listen(5000, () => {
      console.log("Server has started!");
    });
```
**(4) Using Supertest**
 - To use Supertest, you require your app and supertest in the test file.
```javascript
const app = require("../server");
const mongoose = require("mongoose");
const supertest = require("supertest");

beforeEach((done) => {
  mongoose.connect("mongodb://localhost:27017/JestDB",
    { useNewUrlParser: true, useUnifiedTopology: true },
    () => done());
});

afterEach((done) => {
  mongoose.connection.db.dropDatabase(() => {
    mongoose.connection.close(() => done())
  });
});
```

* Those are functions that will be invoked **before and after every single test cases**. This allows us to connect to MongoDB and remove all the data once a test case is finished
* **In Jest**, these are done using **four different** functions:

 - **beforeAll** - called once before all tests.
 - **beforeEach** - called before each of these tests (before every test function).
 - **afterEach** - called after each of these tests (after every test function).
 - **afterAll** - called once after all tests.

**(5) Using routes**
 - We also want to initialize our Express server in app variable which will be accessible from our test case. Let's create **a new test** case called `GET /api/posts`.
```javascript
test("GET /api/posts", async () => {
  const post = await Post.create({ title: "Post 1", content: "Lorem ipsum" });

  await supertest(app).get("/api/posts")
    .expect(200)
    .then((response) => {
      // Check type and length
      expect(Array.isArray(response.body)).toBeTruthy();
      expect(response.body.length).toEqual(1);

      // Check data
      expect(response.body[0]._id).toBe(post.id);
      expect(response.body[0].title).toBe(post.title);
      expect(response.body[0].content).toBe(post.content);
    });
});
```
* Here, we're adding a **new document** to our database so that we won't get an empty response. Then, we send a GET request using SuperTest to the `/api/posts` endpoint and expect the **response status** to be **200**
* which means success. Finally, we check if the response matches the data in the database.

### We can now run our tests by running `npm test`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/raimy3in7xtqklrg35uy.PNG)

## Matchers
**Jest** has quite a few functions used for assertions/expectations. You can see a full list [here](https://jestjs.io/docs/en/expect.html), but these are some common ones.

 - **toBeDefined**
 - **toBeGreaterThan / toBeLessThan**
 - **toBe** (uses === to compare)
 - **toEqual** (for deep object comparison)
 - **toContain** (see if a value is inside of a collection)

## Now, let's test the get single post .

```javascript
test("GET /api/posts/:id", async () => {
  const post = await Post.create({ title: "Post 1", content: "Lorem ipsum" });

  await supertest(app).get("/api/posts/" + post.id)
    .expect(200)
    .then((response) => {
      expect(response.body._id).toBe(post.id);
      expect(response.body.title).toBe(post.title);
      expect(response.body.content).toBe(post.content);
    });
});
```
### We can now run our tests again by running `npm test`

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/1mvul58a5uawa4esdljw.PNG)

# Conclusion! <a name="chapter-4"></a>
I played around with testing lately. One thing I tried to do was to test the endpoints of my Express application.

If you enjoyed this article, please tell a friend about it!
Share it on Twitter. If you spot a typo, I’d appreciate if you can correct it on [GitHub](https://github.com/mohamedlotfe/unit-testing-api-nodejs-jest).
You can contact me through:

Gmail: mhmdlotfy9@gmail.com
or [Linkedin](https://www.linkedin.com/in/mohamedlotfybasher/)
Thank you