Files
uni/slides/dhbw/05_testing.md
T
libretech 4053185e74 dhbw phase 4: alle 8 kapitel slide-MD komplett (4176 zeilen → ~1100 zeilen, ~210 slides total)
kap1 web_eng (104 → 57 slides): kurs-intro + prüfungsleistung-übersicht, internet-timeline (4 milestones), URL→pixel-7-schritte, HTTP, onboarding (node 24 LTS via fnm, brew/winget paket-manager, VS Code/Chromium/Hoppscotch, git setup mit SSH-key, projekt-skeleton), HTML (anatomie, self-closing, grundgerüst, semantik-tabelle, native elemente details/dialog/input-types), A11y volle tiefe (BFSG+EAA 28.06.2025, WCAG-POUR, Perceivable/Operable/Understandable/Robust einzeln mit code, testen mit lighthouse/axe/manuell), CSS-vorschau (anatomie/selektoren/spezifizität/box-model/farben/einheiten/pseudos), JS-vorschau (ECMAScript-timeline/grundlagen/DOM/fetch), frameworks-übersicht (react/vue/svelte/astro counter-vergleich), build-tools/rendering-modi, selbstlernen erste-eigene-seite

kap2 css_extended (13 → 28 slides): box-model + box-sizing global, flexbox basics+achsen+item-properties+typische-patterns, grid basics+areas+auto-layout, flexbox-vs-grid tabelle, mobile-first media queries, viewport-meta, fluid sizing mit clamp(), transitions (was performant), keyframe-animationen, prefers-reduced-motion pflicht, CSS-variablen + dark-mode-pattern, CSS functions (calc/clamp/gradient/oklch/filter), CSS-frameworks tabelle + tailwind pro/contra, selbstlernen flexboxfroggy+cssgridgarden

kap3 nodejs_basics (~22 → 20 slides): was-ist-node + warum-node-tabelle, fnm versions-manager + node 24 LTS + .nvmrc, npm init + package.json (mit type:module), ESM modern + CJS legacy, built-in modules (node: prefix), HTTP-server eingebaut + express, REST mit express (GET/POST/PUT/DELETE-pattern), alternative frameworks tabelle (fastify/hono/koa), routing-pattern mit Router, fetch global ab node 18, error-handling res.ok, environment variables mit dotenv + niemals committen, selbstlernen mini-API

kap4 nodejs_advanced (~24 → 22 slides): synchron-vs-asynchron, promise then-vs-async/await, sequenziell-vs-parallel + Promise.all, promise-methoden tabelle (all/allSettled/race/any), fs/promises lesen+schreiben, streams pipeline für große dateien, DB-übersicht SQLite/Postgres/MongoDB/Redis, better-sqlite3 prepared statements, ORM-übersicht (drizzle/prisma/knex), CRUD-pattern express+sqlite, auth-strategien tabelle, bcrypt für passwörter, JWT-pattern mit httpOnly+secure cookie, try/catch + error-klassen, globaler express-error-handler, selbstlernen REST+DB+auth

kap5 testing (~23 → 23 slides): test-pyramide 70%/20%/10%, vitest setup + describe/it/expect, AAA-pattern, matchers tabelle, TDD red-green-refactor mit konkretem beispiel, supertest für API-integration, test-DB :memory: + lifecycle hooks, mocking mit vi.spyOn (sparsam), playwright E2E mit role-based-locators, E2E-vs-unit tradeoffs, coverage realistisch (80% gut), CI mit github-actions, selbstlernen tests-schreiben

kap6 typescript (~30 → 19 slides): was-ist-TS + warum-tabelle, setup mit tsx + tsconfig.json + strict:true, basis-typen + any vermeiden, object-typen + interfaces + type-aliases, function-typen (Promise<T>), union + discriminated union + intersection, generics + constraints, built-in generics (Partial/Omit/Record), typ-inference (nicht über-annotieren), typen für REST-API mit express, zod runtime-validation + z.infer, any-vs-unknown-vs-never, best practices, selbstlernen API-zu-TS-migration

kap7 docker (~24 → 24 slides): auf-meinem-rechner-läufts problem, container-vs-VM tabelle, docker basics (image/container/dockerfile/registry), dockerfile hallo-welt, schichten + cache-optimierung, multi-stage build, base-image-größen tabelle (alpine ~180MB vs node ~1.1GB), .dockerignore, compose.yml beispiel, compose-commands, networking (service-namen als hostnamen), volumes (named vs bind), env+secrets, github-actions docker-build, registry-übersicht, health-checks, best-practices, selbstlernen app-dockerizen

kap8 best_practices (~30 → 28 slides): sinnvolle commits + conventional commits format, .gitignore template, branches+PRs workflow, README-template mit 6 sektionen, SOLID 5 prinzipien tabelle, S-single-responsibility + D-dependency-inversion mit code, 12-factor wichtigste (codebase/deps/config/processes/disposability), factor 3 config-in-env, factor 6 stateless-processes, SemVer MAJOR.MINOR.PATCH, package.json caret/tilde/exakt, code-reviews geben+nehmen + checkliste, doku-quellen tabelle (MDN/nodejs/npm/tc39/caniuse), stack-overflow gut-nutzen, AI-assistenten pro/contra (verstehen-nicht-kopieren), selbstlernen projekt-hygiene-check

build geht durch alle 8 kapitel-html+pdf
render-spot-check: cover-slide-dark-mode-invert, git-setup code-syntax-highlight, css-anatomie code-blocks — alle sauber

kein deploy, nur build+verify
2026-05-14 17:14:02 +02:00

7.7 KiB
Raw Blame History

marp, theme, paginate, backgroundColor, header, footer, title
marp theme paginate backgroundColor header footer title
true gaia true Web Engineering – DHBW Stuttgart Michael Czechowski – SoSe 2026 Testing
<style> :root { --color-foreground: #1a1a2e; --color-highlight: #d63384; } section.invert { --color-foreground: #fff; } section { font-size: 1.4rem; } h1 { color: #a02060; } section.invert h1 { color: #fff; } h2 { color: #1f2937; } pre { background: #0f0f23; color: #f48fb1; border-radius: 8px; border-left: 3px solid #d63384; } pre code { background: transparent; color: inherit; } code { background: #1a1a2e; color: #f48fb1; padding: 0.15em 0.4em; border-radius: 4px; } a { color: var(--color-highlight); } </style>

Kapitel 5 — Testing

Unit · Integration · E2E · TDD


Warum testen?

Vorteil Was bedeutet das
Confidence Refactor ohne Angst
Regression-Schutz alter Bug taucht nicht wieder auf
Living Documentation Tests zeigen, was Code soll
CI/CD-Voraussetzung automatisch vor jedem Deploy

„Untested Code is broken by design."


Test-Pyramide

        /\
       /E2E\         wenige (langsam, fragil)
      /-----\
     / Integ \       einige (mittel)
    /---------\
   /   Unit    \     viele (schnell, isoliert)
  /-------------\

Faustregel: 70 % Unit, 20 % Integration, 10 % E2E.


Vitest — modernes JS-Testing

npm install -D vitest
// utils.test.js
import { describe, it, expect } from 'vitest';
import { greet } from './utils.js';

describe('greet', () => {
  it('returns greeting with name', () => {
    expect(greet('Ada')).toBe('Hallo Ada');
  });
  
  it('handles empty string', () => {
    expect(greet('')).toBe('Hallo ');
  });
});
npx vitest
npx vitest --coverage

Test-Struktur (AAA)

it('adds two numbers', () => {
  // ARRANGE — Setup
  const a = 2;
  const b = 3;
  
  // ACT — die Operation
  const result = add(a, b);
  
  // ASSERT — Erwartung
  expect(result).toBe(5);
});

Eine Erwartung pro Test (Faustregel).


Vitest-Matchers

Matcher Was
.toBe(x) strict equal (===)
.toEqual(x) deep equal (Objects, Arrays)
.toBeNull() / .toBeUndefined()
.toBeTruthy() / .toBeFalsy()
.toContain(item) Array enthält
.toHaveLength(n)
.toThrow() wirft Fehler
.toMatchSnapshot() Snapshot-Test

TDD — Test-Driven Development


TDD-Cycle: Red → Green → Refactor

  1. Red: Test schreiben (schlägt fehl)
  2. Green: minimaler Code, damit Test besteht
  3. Refactor: Code säubern, Test bleibt grün

Kent Beck, 2002. Steile Lernkurve, aber stark fokussiert.

// 1. Red — Test ohne Implementierung
it('isEven returns true for even', () => {
  expect(isEven(4)).toBe(true);
});
// → ReferenceError: isEven is not defined

// 2. Green — minimale Lösung
function isEven(n) { return n % 2 === 0; }

// 3. Refactor — Edge Cases, Lesbarkeit

Integration-Tests


API testen mit Supertest

import request from 'supertest';
import { app } from './app.js';

describe('GET /users', () => {
  it('returns users array', async () => {
    const res = await request(app).get('/users');
    expect(res.status).toBe(200);
    expect(res.body).toBeInstanceOf(Array);
  });
});

describe('POST /users', () => {
  it('creates a user', async () => {
    const res = await request(app)
      .post('/users')
      .send({ name: 'Ada' });
    expect(res.status).toBe(201);
    expect(res.body.name).toBe('Ada');
  });
});

Test-Datenbank

import { beforeAll, afterEach, afterAll } from 'vitest';
import Database from 'better-sqlite3';

let db;

beforeAll(() => {
  db = new Database(':memory:');  // in-RAM, super schnell
  db.exec('CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)');
});

afterEach(() => {
  db.exec('DELETE FROM users');   // sauberer Start
});

afterAll(() => db.close());

:memory:-DB = jeder Test isoliert, blitzschnell.


Mocking

import { vi, it, expect } from 'vitest';

it('calls fetch with right URL', async () => {
  const mockFetch = vi.spyOn(global, 'fetch').mockResolvedValue({
    ok: true,
    json: async () => ({ name: 'Ada' })
  });
  
  await getUserData(1);
  
  expect(mockFetch).toHaveBeenCalledWith('https://api/users/1');
});

Mock sparsam — übermockt-Tests testen Mocks, nicht Code.


E2E — End-to-End


Playwright — moderne E2E

npm init playwright@latest
import { test, expect } from '@playwright/test';

test('login flow', async ({ page }) => {
  await page.goto('http://localhost:3000');
  await page.getByLabel('Email').fill('ada@example.com');
  await page.getByLabel('Password').fill('secret123');
  await page.getByRole('button', { name: 'Login' }).click();
  
  await expect(page.getByText('Hallo Ada')).toBeVisible();
});

Browser tatsächlich öffnen + interagieren. Realer User-Flow.


Playwright-Locators

// Bevorzugt: rollenbasiert (A11y-konform!)
page.getByRole('button', { name: 'Submit' });
page.getByLabel('Username');
page.getByText('Welcome');

// Fallback: CSS-Selektor
page.locator('.submit-btn');
page.locator('#username');

Rollenbasierte Locators finden Bugs, die A11y betreffen.


E2E vs. Unit — Trade-offs

Unit E2E
Geschwindigkeit ms s
Isolation hoch gering
Realismus gering hoch
Wartung wenig viel
Wann jede Funktion kritische Flows

E2E nicht für jede Detail-Logik — für happy path + Login + Bezahlung.


Coverage + CI


Coverage-Report

npx vitest --coverage
File         | % Stmts | % Branch | % Funcs | Uncovered
-------------+---------+----------+---------+-----------
utils.js     |     100 |      100 |     100 |
api.js       |    75.3 |       60 |      80 | 42-48
auth.js      |    91.2 |       85 |     100 | 15

100 % ≠ keine Bugs. Aber 0 % = sicher Bugs.


Coverage-Ziel realistisch

Code-Typ Realistisch
Pure Funktionen 100 %
Business-Logik 80-90 %
API-Routes 70-80 %
UI-Komponenten 60-70 %
Drittpartei-Wrapper 0 %
Konfig-Files 0 %

Faustregel: über 80 % Gesamt = gut. Unter 50 % = gefährlich.


CI: Tests auf jedem Push

# .github/workflows/test.yml
name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 24
      - run: npm ci
      - run: npm test
      - run: npm run test:e2e

Tests sind grün → PR mergebar. Sonst: rot, mergen verboten.


Selbstlernen — Tests schreiben

Erweitert eure API:

  1. 3+ Unit-Tests für eure Helper-Funktionen (utils.test.js)
  2. 3+ Integration-Tests für eure REST-Endpunkte (mit Supertest)
  3. In-Memory-DB in beforeAll/afterEach
  4. 1 E2E-Test mit Playwright (z.B. Login-Flow)
  5. Coverage-Report generieren

Bonus: GitHub-Actions-Workflow für CI.


Zusammenfassung

Heute gelernt:

  • Test-Pyramide: viele Unit, einige Integration, wenige E2E
  • Vitest als modernes Test-Runner-Setup
  • AAA-Pattern + Matchers + Lifecycle-Hooks
  • TDD-Cycle Red → Green → Refactor
  • Supertest für API-Integration-Tests
  • Playwright für E2E mit rollenbasierten Locators
  • Coverage als Werkzeug, nicht als Ziel
  • CI mit GitHub-Actions

Nächste Stunde: TypeScript — Typen über JavaScript.