How to test a TypeScript Express API with Jest (for Dummies... like me)

nathan_sheryak

Nathan Sheryak

Posted on March 26, 2022

How to test a TypeScript Express API with Jest (for Dummies... like me)

Don't like reading? Only want to see code? Here is the github repo :)

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, Node, Express, SuperTest, and Jest) - 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 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
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
  }
}
Enter fullscreen mode Exit fullscreen mode

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

module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
};
Enter fullscreen mode Exit fullscreen mode

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

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;
Enter fullscreen mode Exit fullscreen mode

server.ts

import app from "./app";

const PORT: Number = 5050;

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

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 };
Enter fullscreen mode Exit fullscreen mode

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"
}
Enter fullscreen mode Exit fullscreen mode

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.

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);
  });
});
Enter fullscreen mode Exit fullscreen mode

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." });
  });
});
Enter fullscreen mode Exit fullscreen mode

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"]);
  });
});
Enter fullscreen mode Exit fullscreen mode

Add a .gitignore

Now as a git cleanliness note, create a .gitignore file.
Image description
In there we can add some files that we want git to ignore:

node_modules
coverage
jest.config.js
dist
Enter fullscreen mode Exit fullscreen mode

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 and GitHub.

💖 💪 🙅 🚩
nathan_sheryak
Nathan Sheryak

Posted on March 26, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related