Skip to content

Automated Tests

Jesse edited this page Dec 7, 2025 · 1 revision

Automated Tests

Automated tests validate that our applications still work as expected after code changes.
In the practicum, we rely on Jest-based test suites and GitHub Actions to automatically run tests for both backend and frontend projects.

This page uses the NSC Events project as a concrete example of how automated testing is wired into our workflows.


1. Overview: Where Tests Run

View Automated Testing Overview

In NSC Events, automated tests run in three main places:

1.1 Local (Developer Machine)

  • You run tests manually with npm scripts
  • Husky pre-push hook runs tests before your code is pushed

1.2 On Push to main (CI)

  • Backend CI (backend-ci.yml)
  • Frontend CI (frontend-ci.yml)
  • Both run tests on the main branch when relevant files change

1.3 On Pull Requests (CI)

  • on-pull-request.yml detects changes for frontend/backend
  • Runs only the relevant CI steps for the changed part of the monorepo

Automated testing is therefore:

  • Preventive: catches issues before merging
  • Selective: runs only where changes happen
  • Consistent: the same scripts run locally and in CI

2. Backend Automated Tests (NestJS)

View Backend Test Workflow

The backend lives in nsc-events-nestjs/ and uses a dedicated CI workflow:

2.1 Backend CI Workflow

File: .github/workflows/backend-ci.yml

name: Backend CI
run-name: NestJS CI

on:
  push:
    branches: [ main ]
    paths:
      - 'nsc-events-nestjs/**'
      - '!nsc-events-nestjs/**/*.md'
      - '!nsc-events-nestjs/LICENSE*'
      - '!nsc-events-nestjs/.gitignore'
      - '!nsc-events-nestjs/.prettierrc'
      - '!nsc-events-nestjs/.eslintrc.js'

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js 22
        uses: actions/setup-node@v3
        with:
          node-version: '22.x'
          cache: 'npm'
          cache-dependency-path: 'package-lock.json'
      - name: Clean install dependencies
        run: HUSKY=0 npm ci
      - name: Run linter
        run: npm run lint
        working-directory: ./nsc-events-nestjs
      - name: Compile typescript
        run: npm run compile
        working-directory: ./nsc-events-nestjs
      - name: Test coverage
        run: npm run test:cov
        working-directory: ./nsc-events-nestjs
      - name: Build App
        run: npm run build --if-present
        working-directory: ./nsc-events-nestjs

2.2 What This Does

For backend changes pushed to main:

  1. Installs dependencies with npm ci
  2. Runs lint to catch style and basic issues
  3. Compiles TypeScript to catch type errors
  4. Runs Jest tests with coverage via npm run test:cov
  5. Builds the app via npm run build

If tests or compilation fail, the workflow fails and flags the problem in GitHub.

2.3 Backend Test Output

The workflow also archives build artifacts and coverage reports, which can be downloaded from the GitHub Actions run.


3. Frontend Automated Tests (Next.js)

View Frontend Test Workflow

The frontend lives in nsc-events-nextjs/ and has its own CI workflow:

3.1 Frontend CI Workflow

File: .github/workflows/frontend-ci.yml

name: Frontend CI
run-name: NextJS CI

on:
  push:
    branches: [ main ]
    paths:
      - 'nsc-events-nextjs/**'
      - '!nsc-events-nextjs/**/*.md'
      - '!nsc-events-nextjs/LICENSE*'
      - '!nsc-events-nextjs/.gitignore'
      - '!nsc-events-nextjs/.prettierrc'
      - '!nsc-events-nextjs/.eslintrc.js'

jobs:
  build-and-test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v3
      - name: Use Node.js 22
        uses: actions/setup-node@v3
        with:
          node-version: "22.x"
          cache: "npm"
          cache-dependency-path: 'package-lock.json'
      - name: Clean install dependencies
        run: HUSKY=0 npm ci
      - name: Run linter
        run: npm run lint
        working-directory: ./nsc-events-nextjs
      - name: Compile typescript
        run: npm run compile
        working-directory: ./nsc-events-nextjs
      - name: Jest unit tests
        run: npm run test
        working-directory: ./nsc-events-nextjs
      - name: Test coverage
        run: npm run test:coverage
        working-directory: ./nsc-events-nextjs
      - name: Build App
        run: npm run build
        working-directory: ./nsc-events-nextjs

3.2 What This Does

For frontend changes on main:

  1. Installs dependencies with npm ci
  2. Runs ESLint via npm run lint
  3. Compiles TypeScript via npm run compile
  4. Runs Jest unit tests via npm run test
  5. Optionally generates coverage via npm run test:coverage
  6. Builds the Next.js app via npm run build

Like the backend, any failure (lint, tests, compile, build) causes the CI check to fail.


4. Automated Tests on Pull Requests (Monorepo-Aware)

View PR-Based Test Behavior

NSC Events uses a monorepo-aware CI workflow for pull requests, so we only run tests for the parts of the repo that changed.

4.1 Change Detection

File: .github/workflows/on-pull-request.yml
The dorny/paths-filter action checks whether frontend and/or backend files were modified:

jobs:
  detect-changes:
    runs-on: ubuntu-latest
    outputs:
      frontend: ${{ steps.filter.outputs.frontend }}
      backend: ${{ steps.filter.outputs.backend }}
    steps:
      - uses: actions/checkout@v3
      - uses: dorny/paths-filter@v2
        id: filter
        with:
          filters: |
            frontend:
              - 'nsc-events-nextjs/**'
              - '!nsc-events-nextjs/**/*.md'
              - '!nsc-events-nextjs/LICENSE*'
              - '!nsc-events-nextjs/.gitignore'
              - '!nsc-events-nextjs/.prettierrc'
              - '!nsc-events-nextjs/.eslintrc.js'
            backend:
              - 'nsc-events-nestjs/**'
              - '!nsc-events-nestjs/**/*.md'
              - '!nsc-events-nestjs/LICENSE*'
              - '!nsc-events-nestjs/.gitignore'
              - '!nsc-events-nestjs/.prettierrc'
              - '!nsc-events-nestjs/.eslintrc.js'

If backend code changed, backend becomes true. Same for frontend.

4.2 Conditional Jobs

Then we conditionally run the relevant CI job(s):

frontend-ci:
  needs: detect-changes
  if: needs.detect-changes.outputs.frontend == 'true'
  # ... (lint, compile, test, build)

backend-ci:
  needs: detect-changes
  if: needs.detect-changes.outputs.backend == 'true'
  # ... (lint, compile, test:cov, build)

4.3 Why This Matters

  • Faster PR checks
  • No unnecessary test runs for unaffected code
  • Still guarantees coverage for changed areas

5. How to Run Tests Manually

View Manual Test Commands

Backend (NestJS)

From the repo root, or nsc-events-nestjs directory:

cd nsc-events-nestjs
npm run test        # run backend unit tests
npm run test:cov    # run tests with coverage report

Frontend (Next.js)

From the repo root, or nsc-events-nextjs directory:

cd nsc-events-nextjs
npm run test           # run frontend unit tests
npm run test:coverage  # run tests with coverage report

General Recommendations

  • Run tests locally before pushing large feature branches
  • Always run npm run test after major refactors
  • Use coverage reports to identify untested critical paths

Clone this wiki locally