How to test a TypeScript Express API with Jest (for Dummies... like me)
Tags: typescript, jest, express, node
Published: 2022-03-26 20:19:32

_Don't like reading? Only want to see code? Here is the [github repo](https://github.com/N8ers/ts-express-jest-supertest) :)_

## Opening
If you've been scratching your head trying to test your new TypeScript Express API - I've been there. And I'd love to save you some time.

I was trying my hand at converting a Node and Express api to use TypeScript. All was going well until I got to testing and I started having all of these existential questions. Like `do I need to 'build' my test files?`, `do my config files need to be 'built'?`, and `why did i decide to use TypeScript when my API already worked!?`.

This article can answer *some* of those questions. It also assumes you know a little bit about the technologies the project uses ([TypeScript](https://www.typescriptlang.org/), [Node](https://nodejs.org/en/), [Express](https://expressjs.com/), [SuperTest](https://github.com/visionmedia/supertest#readme), and [Jest](https://jestjs.io/)) - this is more of a project structure guide than an in-depth look at the technologies used.

## Initialize project and import the imports
* Create a directory for your project and `cd` into it.
* Use [NPM](https://docs.npmjs.com/) to initialize the project `npm init -y`.
* Import dependencies `npm i express`.
* Import dev-dependencies `npm i --save-dev typescript supertest nodemon jest ts-jest ts-node @types/jest @types/supertest @types/express`.

## Initialize TypeScript
Now let's add TypeScript to our project.
`npx tsc --init`
The above command will generate a `tsconfig.json` file. 
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kheeahvk0ljkfbg7hca0.jpg)
You'll want to modify it with the below. Not every item is necessary, feel free to further configure it to match your needs.
A quick note on the `exclude` value, these are files that the build will ignore. Not all of them exist yet ;)
```
{
  "exclude": ["./coverage", "./dist", "__tests__", "jest.config.js"],
  "ts-node": {
    "transpileOnly": true,
    "files": true
  },
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "rootDir": "./src",
    "moduleResolution": "node",
    "checkJs": true,
    "outDir": "./dist",
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "noImplicitAny": true,
    "skipLibCheck": true
  }
}
```

## Initialize Jest
Up next, we want to add the Jest testing framework to our project.
`npx ts-jest config:init`
The above command will generate a `jest.config.js` file. You'll want to modify it with the below, so it works with `ts-jest` (this is what makes jest work with TypeScript).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1i5t0rifj57h9nqdw8zj.jpg)

```
module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
};
```

## Create a basic Express app with TypeScript
We'll need to create a `src` directory with two TypeScript files in it: `app.ts` and `server.ts`. In the `src` directory, we want to add another directory: `routes`. In the `routes` directory we want to add a `user.routes.ts` file.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tbin9bkrumyvs3p85zcd.jpg)
 

`app.ts`

```
import express, { Application, Request, Response, NextFunction } from "express";

import { router as userRoutes } from "./routes/user.routes";

const app: Application = express();

app.use("/users", userRoutes);

app.use("/", (req: Request, res: Response, next: NextFunction): void => {
  res.json({ message: "Allo! Catch-all route." });
});

export default app;
```

`server.ts`
```
import app from "./app";

const PORT: Number = 5050;

app.listen(PORT, (): void => console.log(`running on port ${PORT}`));
```

`user.routes.ts`
```
import { Router, Request, Response } from "express";

const router = Router();

router.get("/", (req: Request, res: Response): void => {
  let users = ["Goon", "Tsuki", "Joe"];
  res.status(200).send(users);
});

export { router };
```

## Configure `package.json`
Let's configure our `package.json` to use our new tools! To the `scripts` section add the following:
```
scripts: {
  "test": "jest --coverage",
  "dev": "nodemon ./src/server.ts",
  "build": "tsc"
}
```


## Making sure our API is working
Now let's be sure we haven't made any mistakes so far. Run the command `npm run dev`. Open a browser and go to `http://localhost:5050/`. You should be greeted with the welcome message we defined on line 10 of app.js `Allo! Catch-all route.`. Now try out our user route `http://localhost:5050/users`, where you should find a list of our users from user.routes.ts `["Goon", "Tsuki", "Joe"]`.

## Writing our tests
Now for the moment you've been waiting for... testing.
in our project add a `__tests__` directory. In that directory we'll duplicate the file structure we made in the `src` directory. Creating a `app.test.ts`, `server.test.ts`, and `routes/user.routes.test.ts`.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2issq2hp0kshqlu9z2dr.jpg).

Let's write our first test, just to make sure jest is working.
`server.test.ts`
```
describe("Server.ts tests", () => {
  test("Math test", () => {
    expect(2 + 2).toBe(4);
  });
});
```
Now we'll us SuperTest to make a network request test.
`app.test.ts`
```
import request from "supertest";

import app from "../src/app";

describe("Test app.ts", () => {
  test("Catch-all route", async () => {
    const res = await request(app).get("/");
    expect(res.body).toEqual({ message: "Allo! Catch-all route." });
  });
});
```
Now our last test will test our `users` route.
`user.routes.test.ts`
```
import request from "supertest";

import app from "../../src/app";

describe("User routes", () => {
  test("Get all users", async () => {
    const res = await request(app).get("/users");
    expect(res.body).toEqual(["Goon", "Tsuki", "Joe"]);
  });
});
```

## Add a `.gitignore`
Now as a git cleanliness note, create a `.gitignore` file.
![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vni2im2ojblvbnv05b7y.jpg)
In there we can add some files that we want git to ignore:
```
node_modules
coverage
jest.config.js
dist
```

## Closing
Setting up testing in a TypeScript/Express API took me a considerable amount of time. And I was really surprised how few resources I found. I hope this helps you in any TypeScript testing predicament you might find your self in. 
I'm not a TypeScript authority, I'm just happy I was able to get this working. So if you have notes on what your own setup is like, or advice on making this setup better - feel free to reach out or comment :)

If you liked the article or want to see more of my work, feel free to check out my [portfolio](https://nathansheryak.com/) and [GitHub](https://github.com/N8ers).