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

384 lines
7.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
marp: true
theme: gaia
paginate: true
backgroundColor: #fff
header: "Web Engineering – DHBW Stuttgart"
footer: "Michael Czechowski – SoSe 2026"
title: 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>
<!-- _class: lead -->
# 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
```sh
npm install -D vitest
```
```javascript
// 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 ');
});
});
```
```sh
npx vitest
npx vitest --coverage
```
---
# Test-Struktur (AAA)
```javascript
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 |
---
<!-- _class: lead -->
# 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.
```javascript
// 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
```
---
<!-- _class: lead -->
# Integration-Tests
---
# API testen mit Supertest
```javascript
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
```javascript
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
```javascript
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.
---
<!-- _class: lead -->
# E2E — End-to-End
---
# Playwright — moderne E2E
```sh
npm init playwright@latest
```
```javascript
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
```javascript
// 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**.
---
<!-- _class: lead -->
# Coverage + CI
---
# Coverage-Report
```sh
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
```yaml
# .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.
---
<!-- _class: aufgabe -->
# 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.