How to test a TypeScript Express API with Jest (for Dummies... like me)
Nathan Sheryak
Posted on March 26, 2022
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.
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).
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.
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
.
.
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.
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 and GitHub.
Posted on March 26, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.