DEV Community

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

Posted on

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.

Top comments (7)

Collapse
 
someone_924 profile image
Dj

Thanks for sharing. I wander why duplicate the file structure in the src directory to test directory?

Collapse
 
nathan_sheryak profile image
Nathan Sheryak

@sayandcode is totally right, where you put the test files is up to you.

Some people like to make a directory for each component where they keep the component, css, and test files. I think there is a good argument for keeping files and tests next to each other as it forces developers to remember that they have tests and should keep the updated! Having tests in their own directory can lead to them being overlooked and neglected.

Collapse
 
sayandcode profile image
sayandcode

I think you can also just put the test side by side to the actual file if you want. Then we can remove all references to test

Collapse
 
gonghaima profile image
Steven Gong

In my experience, I've come across both approaches when it comes to organizing test files. Some people prefer to keep them in the same location as the code, using file name patterns like ".spec." or ".test.", while others prefer to keep them in a separate folder. It really depends on personal preference and what works best for the project.

Collapse
 
sayandcode profile image
sayandcode

Thank you for this! I was shying away from using Jest thinking it would be hard to setup in my existing project. You just showed me how to do it so simply!

Collapse
 
gonghaima profile image
Steven Gong

Wow, I really appreciate the simplicity of the settings and code you've shared! I just upvoted and starred it on GitHub. Keep up the great work of sharing your knowledge and skills with others!

Collapse
 
mdwiltfong profile image
mdwiltfong

Ah thank you so much for writing this. Almost gave up on writing tests.