branch-coexist: 3 neue decks 223015b-2026 / 223015c-2026 / dhbw-2026 daneben main

per user-request einen branch der main-content UND rewrite-content trägt — saubere separierung statt symlink/merge-conflict.

struktur:
- slides/<course>/ → main-content restored, eigene assets (kein new-demo-leak)
- slides/<course>-2026/ → rewrite-content, eigene assets (komplett kopiert + new demos)

Makefile: COURSES += 223015b-2026 223015c-2026 dhbw-2026 mit eigenen NAME/KAPITEL/DEPLOY/KLAUSUR-konfig. Klausur für b+c=1, dhbw=leer. Deploy-paths /hdm/223015b-2026 etc.

make build durchlaufen für alle 6 decks, 0 fehler.

dedup: 223015c+dhbw assets identisch in beiden ordnern (kein neu-demos auf b ergänzt). 223015b 86 → 120 files in -2026 (= +34 neue eroeffner/usb-c-drei-achsen/container-vs-codec/etc).
This commit is contained in:
libretech committed 2026-05-14 21:54:15 +02:00
1 parent 6209f9f3a0
commit 351e3dfc23
467 files changed
+32391 -4701

No files matched your search

+383
View File
@@ -0,0 +1,383 @@
---
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.