357 lines
7.1 KiB
Markdown
357 lines
7.1 KiB
Markdown
---
|
||
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: #005f8a;
|
||
--color-dimmed: #4a6a7a;
|
||
}
|
||
section.invert {
|
||
--color-foreground: #fff;
|
||
}
|
||
section {
|
||
font-size: 1.7rem;
|
||
}
|
||
h1 {
|
||
color: #005f8a;
|
||
}
|
||
section.invert h1 {
|
||
color: #fff;
|
||
}
|
||
pre {
|
||
background: #0f1e2e;
|
||
color: #00b4d8;
|
||
border-radius: 8px;
|
||
border-left: 3px solid #005f8a;
|
||
}
|
||
pre code {
|
||
background: transparent;
|
||
color: inherit;
|
||
}
|
||
code {
|
||
background: #1a3a4a;
|
||
color: #00b4d8;
|
||
padding: 0.15em 0.4em;
|
||
border-radius: 4px;
|
||
}
|
||
a {
|
||
color: var(--color-highlight);
|
||
}
|
||
</style>
|
||
|
||
<!-- _class: invert -->
|
||
<!-- _header: '' -->
|
||
<!-- _backgroundColor: #001520 -->
|
||
|
||
# Testing
|
||
|
||
## Unit · Integration · End-to-End
|
||
|
||
---
|
||
|
||
# Inhalt
|
||
|
||
1. Test Automation Pyramid
|
||
2. Test Automation Trophy
|
||
3. Static Code Analysis
|
||
4. Unit Tests
|
||
5. Integration Tests
|
||
6. End-to-End Tests (API + Client)
|
||
7. Live Coding
|
||
|
||
---
|
||
|
||
# Test Automation Pyramid
|
||
|
||
```
|
||
▲ manual / exploratory
|
||
▲ ▲ E2E (UI + API)
|
||
▲▲▲▲ Integration (class + real deps)
|
||
▲▲▲▲▲▲ Unit (class + mocked deps)
|
||
▲▲▲▲▲▲▲▲ Static analysis (compiler, linter)
|
||
```
|
||
|
||
- Oben: wenige, langsam, teuer, brüchig
|
||
- Unten: viele, schnell, billig, stabil
|
||
|
||
---
|
||
|
||
# Test Automation V-Modell
|
||
|
||
```
|
||
Requirements ────────────► Acceptance Test
|
||
▼ ▲
|
||
Architecture ────────► Integration Test
|
||
▼ ▲
|
||
Modular Design ──────► Unit Test
|
||
▼ ▲
|
||
Implementation
|
||
```
|
||
|
||
Jede Design-Phase hat Testpendant. Testfälle früh ableiten.
|
||
|
||
---
|
||
|
||
# Test Automation Trophy (Kent C. Dodds)
|
||
|
||
```
|
||
🏆
|
||
┌─────────┐
|
||
│ E2E │ ← klein
|
||
├─────────┤
|
||
│ Integr. │ ← GROSS (Hauptfokus)
|
||
├─────────┤
|
||
│ Unit │ ← mittel
|
||
├─────────┤
|
||
│ Static │ ← Basis (TS, ESLint)
|
||
└─────────┘
|
||
```
|
||
|
||
Andere Gewichtung: **Integration im Zentrum**, Static als breite Basis.
|
||
|
||
---
|
||
|
||
# Testing Frameworks Übersicht
|
||
|
||
**Static Code Analysis**
|
||
- ESLint, Prettier, SonarQube, TypeScript
|
||
|
||
**Unit + Integration**
|
||
- jest, mocha, **vitest**
|
||
|
||
**End-to-End**
|
||
- **Cypress**, **Playwright**, supertest (API only)
|
||
|
||
**Visual / Interaction**
|
||
- Storybook, Chromatic
|
||
|
||
---
|
||
|
||
# Static Code Analysis
|
||
|
||
- Läuft in Millisekunden
|
||
- Findet Syntaxfehler, ungenutzte Variablen, undefined refs
|
||
- Kein Runtime-Overhead
|
||
- **Erste Verteidigungslinie**
|
||
|
||
```bash
|
||
npm i -D eslint prettier
|
||
npx eslint --init
|
||
npx prettier --write .
|
||
```
|
||
|
||
`SonarQube`: zentraler Server, Code Smells, Security, Coverage.
|
||
|
||
---
|
||
|
||
# Unit Tests – Eigenschaften
|
||
|
||
- **Isolation** – jede Dependency gemockt
|
||
- **Deterministisch** – gleicher Input → gleicher Output
|
||
- **Schnell** – kein I/O, kein Netzwerk, kein FS
|
||
- **Fokussiert** – eine Funktion pro Test
|
||
|
||
---
|
||
|
||
# Unit Test – Beispiel (vitest)
|
||
|
||
```javascript
|
||
// cache.unit.test.js
|
||
import { describe, it, expect } from "vitest";
|
||
import { Cache } from "./cache.js";
|
||
|
||
describe("Cache", () => {
|
||
it("creates and retrieves data with custom key", () => {
|
||
const cache = new Cache();
|
||
const data = { user: "wolfgang", city: "stuttgart" };
|
||
|
||
const key = cache.create(data, "custom-key");
|
||
|
||
expect(key).toBe("custom-key");
|
||
expect(cache.get("custom-key")).toEqual(data);
|
||
});
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
# Integration Tests – Eigenschaften
|
||
|
||
- **Reale Komponenten** – echte Cache + Express-Instanzen
|
||
- **Supertest-Magie** – simuliert HTTP ohne Server-Startup
|
||
- **In-Process** – alles im selben Node-Prozess
|
||
- **Kein Netzwerk** – keine TCP-Connections
|
||
|
||
→ teste Zusammenspiel mehrerer Module ohne Deployment.
|
||
|
||
---
|
||
|
||
# Integration Test – Beispiel (supertest)
|
||
|
||
```javascript
|
||
// app.integration.test.js
|
||
import request from "supertest";
|
||
import { app, cache } from "./app.js";
|
||
|
||
test("full CRUD lifecycle with real cache", async () => {
|
||
const res = await request(app)
|
||
.post("/api/entries")
|
||
.send({ name: "integration-test", status: "active" });
|
||
|
||
expect(res.status).toBe(201);
|
||
const { key } = res.body;
|
||
|
||
expect(cache.keys()).toContain(key);
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
# End-to-End Tests (API)
|
||
|
||
- **Echter Server** – HTTP auf TCP-Port
|
||
- **Echtes Netzwerk** – `fetch` über localhost
|
||
- **Echte Umgebung** – CORS, Middleware-Stack
|
||
- **User-Perspektive** – exakt was Clients sehen
|
||
|
||
---
|
||
|
||
# E2E API Test – Beispiel
|
||
|
||
```javascript
|
||
// index.e2e.test.js
|
||
import { createServer } from "node:http";
|
||
import { app } from "./app.js";
|
||
|
||
let server;
|
||
const TEST_PORT = 8081;
|
||
const baseUrl = `http://localhost:${TEST_PORT}`;
|
||
|
||
beforeAll(async () => {
|
||
server = createServer(app);
|
||
await new Promise((r) => server.listen(TEST_PORT, r));
|
||
});
|
||
|
||
afterAll(() => server.close());
|
||
|
||
test("complete user journey", async () => {
|
||
const res = await fetch(`${baseUrl}/api`, {
|
||
method: "POST",
|
||
headers: { "Content-Type": "application/json" },
|
||
body: JSON.stringify({ message: "e2e", timestamp: Date.now() }),
|
||
});
|
||
expect(res.status).toBe(201);
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
# End-to-End Tests (Client)
|
||
|
||
- **Echter Browser** – Chromium / Firefox / WebKit
|
||
- **Echtes DOM** – Klicks, Tippen, Scrollen
|
||
- **Echtes Rendering** – CSS, async Operationen
|
||
- **Cross-Origin** – Client ↔ Server-Kommunikation
|
||
|
||
Tools: **Cypress**, **Playwright**
|
||
|
||
---
|
||
|
||
# Cypress – Minimalbeispiel
|
||
|
||
```javascript
|
||
// cypress/e2e/counter.cy.js
|
||
describe("Counter App", () => {
|
||
it("increments on click", () => {
|
||
cy.visit("http://localhost:3000");
|
||
cy.contains("button", "Clicked 0 times").click();
|
||
cy.contains("button", "Clicked 1 times");
|
||
});
|
||
});
|
||
```
|
||
|
||
```bash
|
||
npx cypress open # GUI
|
||
npx cypress run # headless / CI
|
||
```
|
||
|
||
---
|
||
|
||
# Playwright – Minimalbeispiel
|
||
|
||
```javascript
|
||
// tests/counter.spec.js
|
||
import { test, expect } from "@playwright/test";
|
||
|
||
test("counter increments", async ({ page }) => {
|
||
await page.goto("http://localhost:3000");
|
||
await page.getByRole("button").click();
|
||
await expect(page.getByRole("button")).toContainText("Clicked 1 times");
|
||
});
|
||
```
|
||
|
||
```bash
|
||
npx playwright test
|
||
npx playwright test --ui
|
||
```
|
||
|
||
---
|
||
|
||
# Coverage Reports
|
||
|
||
```bash
|
||
npx vitest run --coverage
|
||
```
|
||
|
||
```
|
||
File | % Stmts | % Branch | % Funcs | % Lines
|
||
--------------|---------|----------|---------|--------
|
||
cache.js | 95.2 | 80.0 | 100.0 | 95.2
|
||
app.js | 88.7 | 75.0 | 90.0 | 88.7
|
||
```
|
||
|
||
- `lcov.info` → CI / SonarQube
|
||
- HTML Report → `coverage/index.html`
|
||
|
||
---
|
||
|
||
# Best Practices
|
||
|
||
- **AAA**: Arrange · Act · Assert
|
||
- Tests sind **Dokumentation** – beschreibend benennen
|
||
- Ein Verhalten pro Test
|
||
- Keine Logik im Test (keine `if`/`for`)
|
||
- **Test Doubles**: stub, mock, spy, fake
|
||
- CI: Tests bei jedem Push
|
||
|
||
---
|
||
|
||
# Live Coding
|
||
|
||
Repo: `node-cache-api`
|
||
|
||
```bash
|
||
git clone https://github.com/nextlevelshit/node-cache-api
|
||
cd node-cache-api
|
||
npm install
|
||
npm test # unit + integration
|
||
npm run test:e2e # e2e
|
||
```
|
||
|
||
---
|
||
|
||
<!-- _class: invert -->
|
||
<!-- _backgroundColor: #001520 -->
|
||
|
||
# Fragen?
|
||
|
||
**Hausaufgabe:** Eigene Projektidee mit mind. 1 Unit + 1 Integration Test starten.
|