Testing and Deploying Web Apps
Testing pyramid
| Level | Scope | Tools | Share |
|---|---|---|---|
| Unit | One function/component | Vitest, Jest | Most |
| Integration | Several modules or an API route | Supertest, Testing Library | Some |
| End-to-end | The whole app in a browser | Playwright, Cypress | Few, critical flows |
Unit test example
import { describe, it, expect } from "vitest";
import { total } from "./cart";
describe("total", () => {
it("adds item prices", () => {
expect(total([{ price: 10 }, { price: 5 }])).toBe(15);
});
it("returns 0 for an empty cart", () => {
expect(total([])).toBe(0);
});
});
API test example
import request from "supertest";
import app from "./app";
it("rejects an empty note", async () => {
const res = await request(app).post("/api/notes").send({ text: "" });
expect(res.status).toBe(422);
});
Continuous integration
Run lint, tests and build on every push.
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: npm
- run: npm ci
- run: npm run lint
- run: npm test
- run: npm run build
Docker
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --omit=dev
COPY . .
ENV NODE_ENV=production
EXPOSE 3000
CMD ["node", "src/server.js"]
Deployment options
| Option | Good for |
|---|---|
| Static hosting / CDN (Vercel, Netlify, GitHub Pages) | Front-end and static sites |
| Platform-as-a-service (Render, Railway, Fly.io) | Node/Python APIs with managed databases |
| Containers on a cloud provider | Full control and scaling |
Release checklist
- Environment variables set in the host, not in the repo
- Database migrations applied
- Health-check endpoint (
/health) and logging - HTTPS and a custom domain
- Rollback plan (previous version redeployable)
Loading Comments...