Angular-Anwendungen werden nicht nur entwickelt, sondern auch kontinuierlich getestet. Automated Testing ist kein optionales Add-on, sondern integraler Bestandteil professioneller Softwareentwicklung. Das Angular-Team hat Jasmine als Testing-Framework und Karma als Test-Runner standardisiert, eine bewährte Kombination, die sich über Jahre in der Produktion etabliert hat.
Diese Toolchain löst ein fundamentales Problem: Wie testet man komplexe, zustandsbehaftete Browser-Anwendungen automatisiert, reproduzierbar und in verschiedenen Umgebungen? Die Antwort liegt in der Arbeitsteilung. Jasmine definiert, wie Tests geschrieben werden – die Syntax, die Assertions, die Mocking-Mechanismen. Karma kümmert sich um die Ausführung – Browser-Management, File-Watching, Reporting.
Der Workflow ist klar: Tests werden in Jasmine-Syntax geschrieben, Karma lädt diese Tests in echte Browser, führt sie aus und sammelt die Ergebnisse. Diese Architektur ermöglicht Cross-Browser-Testing ohne manuelle Interaktion.
Jasmine folgt dem Behavior-Driven Development-Ansatz. Tests beschreiben Verhalten in natürlicher Sprache, nicht technische Implementierungsdetails. Ein Calculator wird nicht “getestet”, sondern “sollte zwei Zahlen addieren können”. Diese Formulierung macht Tests lesbarer und dokumentiert gleichzeitig die erwartete Funktionalität.
Die grundlegende Struktur jedes Jasmine-Tests:
describe('Calculator', () => {
let calculator: Calculator;
beforeEach(() => {
calculator = new Calculator();
});
it('should add two numbers correctly', () => {
const result = calculator.add(2, 3);
expect(result).toBe(5);
});
it('should handle negative numbers', () => {
const result = calculator.add(-5, 3);
expect(result).toBe(-2);
});
});Der describe-Block gruppiert zusammengehörige Tests zu
einer Suite. Die String-Beschreibung identifiziert den Testgegenstand.
Innerhalb der Suite definiert it einzelne Spezifikationen –
konkrete, testbare Aussagen über das Verhalten. Die
expect-Funktion mit Matchers formuliert Assertions –
Bedingungen, die erfüllt sein müssen.
Die beforeEach-Funktion läuft vor jedem Test. Sie stellt
sicher, dass jeder Test mit frischem State beginnt. Dies verhindert
Interferenzen – ein fehlgeschlagener Test beeinflusst nicht nachfolgende
Tests. Die Isolation ist essentiell für zuverlässige Testsuiten.
Komplexe Komponenten erfordern strukturierte Tests. Jasmine
unterstützt verschachtelte describe-Blöcke für
hierarchische Organisation:
describe('UserService', () => {
let service: UserService;
let httpMock: HttpTestingController;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule],
providers: [UserService]
});
service = TestBed.inject(UserService);
httpMock = TestBed.inject(HttpTestingController);
});
describe('Authentication', () => {
it('should login with valid credentials', () => {
const credentials = { username: 'user', password: 'pass' };
service.login(credentials).subscribe(result => {
expect(result.token).toBeDefined();
expect(result.user.username).toBe('user');
});
const req = httpMock.expectOne('/api/auth/login');
req.flush({ token: 'abc123', user: { username: 'user' } });
});
it('should reject invalid credentials', () => {
const credentials = { username: 'user', password: 'wrong' };
service.login(credentials).subscribe(
() => fail('should have failed'),
error => expect(error.status).toBe(401)
);
const req = httpMock.expectOne('/api/auth/login');
req.flush('Unauthorized', { status: 401, statusText: 'Unauthorized' });
});
});
describe('User Management', () => {
it('should fetch user profile', () => {
service.getProfile(123).subscribe(user => {
expect(user.id).toBe(123);
expect(user.email).toBeDefined();
});
const req = httpMock.expectOne('/api/users/123');
req.flush({ id: 123, email: 'user@example.com' });
});
});
afterEach(() => {
httpMock.verify();
});
});Die verschachtelte Struktur spiegelt funktionale Gruppierung wider.
Authentication-Tests sind separiert von User-Management-Tests. Jede
Gruppe kann eigene Setup-Logik haben, während gemeinsame Initialisierung
im äußeren beforeEach bleibt.
Jasmine bietet vier Lifecycle-Hooks mit unterschiedlichen Ausführungszeitpunkten:
| Hook | Ausführung | Verwendung |
|---|---|---|
beforeEach |
Vor jedem Test | Test-Daten initialisieren, Mocks aufsetzen |
afterEach |
Nach jedem Test | Aufräumen, Subscriptions beenden |
beforeAll |
Einmal vor allen Tests | Teure Ressourcen erstellen (DB-Connections) |
afterAll |
Einmal nach allen Tests | Ressourcen freigeben |
Die Wahl zwischen beforeEach und beforeAll
hat Performance-Implikationen. beforeAll ist schneller, da
Setup nur einmal läuft. Tests teilen sich jedoch State, was zu
Interferenzen führen kann. beforeEach garantiert Isolation,
kostet aber Performance bei teuren Operations.
describe('Database Operations', () => {
let db: Database;
let connection: Connection;
beforeAll(async () => {
connection = await createConnection({
type: 'sqlite',
database: ':memory:'
});
await connection.synchronize();
});
beforeEach(() => {
db = new Database(connection);
});
afterEach(async () => {
await connection.query('DELETE FROM users');
});
afterAll(async () => {
await connection.close();
});
it('should insert user', async () => {
const user = await db.createUser({ name: 'John' });
expect(user.id).toBeDefined();
});
it('should find user by id', async () => {
const created = await db.createUser({ name: 'Jane' });
const found = await db.findUser(created.id);
expect(found.name).toBe('Jane');
});
});Die Connection wird einmal erstellt und geteilt. Zwischen Tests werden Daten gelöscht, um Isolation zu gewährleisten. Nach allen Tests wird die Connection geschlossen.
Jasmine’s Matcher-System bietet spezifische Assertions für verschiedene Datentypen und Szenarien. Die Wahl des richtigen Matchers macht Tests ausdrucksstärker und Fehlermeldungen klarer.
Grundlegende Gleichheitsprüfung unterscheidet zwischen Identität und struktureller Gleichheit:
// Identität: Strikte Gleichheit (===)
expect(value).toBe(5);
expect(reference1).toBe(reference2);
// Strukturelle Gleichheit: Tiefe Objektvergleiche
expect(user).toEqual({ id: 1, name: 'John' });
expect(array).toEqual([1, 2, 3]);toBe prüft Referenz-Gleichheit. Zwei Objekte mit
identischem Inhalt sind nicht toBe gleich, wenn sie
unterschiedliche Instanzen sind. toEqual vergleicht Inhalte
rekursiv – ideal für Objekte und Arrays.
Numerische Vergleiche unterstützen Präzisionsprobleme bei Floating-Point-Arithmetik:
expect(value).toBeGreaterThan(10);
expect(value).toBeLessThanOrEqual(100);
// Floating-Point-Vergleich mit Toleranz
expect(Math.PI).toBeCloseTo(3.14159, 5); // 5 DezimalstellentoBeCloseTo ist essentiell für numerische Berechnungen.
Floating-Point-Arithmetik ist inhärent unpräzise –
0.1 + 0.2 ergibt nicht exakt 0.3. Der zweite
Parameter definiert die erwartete Genauigkeit.
Wahrheitswert-Tests decken JavaScript’s komplexe Truthiness ab:
expect(value).toBeTruthy(); // != false, 0, '', null, undefined, NaN
expect(value).toBeFalsy(); // == false, 0, '', null, undefined, NaN
expect(value).toBeNull(); // === null
expect(value).toBeUndefined(); // === undefined
expect(value).toBeDefined(); // !== undefinedDie Unterscheidung ist subtil aber wichtig. toBeTruthy()
matched viele Werte, toBeDefined() nur Nicht-Undefined. Die
Wahl kommuniziert Intention – erwartet der Test einen konkreten Wert
oder nur “irgendwas außer undefined”?
Collection-Tests prüfen Inhalte ohne exakte Reihenfolge:
expect(array).toContain('element');
expect(array).toHaveSize(5);
expect(string).toMatch(/pattern/);
expect(string).toContain('substring');Exception-Tests verifizieren Error-Handling:
expect(() => throwingFunction()).toThrow();
expect(() => throwingFunction()).toThrowError('specific message');
expect(() => throwingFunction()).toThrowError(TypeError);Die Funktion wird als Callback übergeben, nicht direkt aufgerufen. Jasmine fängt die Exception und validiert sie. Ohne Callback würde die Exception den Test abbrechen.
Negation invertiert jeden Matcher:
expect(value).not.toBe(6);
expect(array).not.toContain('missing');
expect(() => safeFunction()).not.toThrow();Angular-Anwendungen sind asynchron. HTTP-Requests, Timers, Promises, Observables – alles läuft außerhalb des synchronen Flows. Tests müssen diese Asynchronität handhaben, sonst schlagen sie fehl oder produzieren false positives.
Jasmine bietet drei Mechanismen für asynchrone Tests. Der klassische
done-Callback:
it('should load user data', (done) => {
userService.getUser(123).subscribe(user => {
expect(user.id).toBe(123);
expect(user.name).toBeDefined();
done();
});
});Der done-Parameter signalisiert Jasmine, dass der Test
asynchron ist. Jasmine wartet, bis done() aufgerufen wird.
Fehlt der Aufruf, timeoutet der Test. Passiert eine Exception vor
done(), schlägt der Test fehl.
Die moderne Promise-basierte Syntax ist eleganter:
it('should load user data', async () => {
const user = await userService.getUser(123).toPromise();
expect(user.id).toBe(123);
expect(user.name).toBeDefined();
});Das async-Keyword macht die Test-Funktion zu einer async
Function. Jasmine erkennt dies und wartet auf Promise-Resolution.
Exceptions werden automatisch gefangen und führen zum Test-Failure.
Für Observable-Tests bietet Angular’s fakeAsync und
tick:
import { fakeAsync, tick } from '@angular/core/testing';
it('should debounce search input', fakeAsync(() => {
let result: string;
searchService.search('query').subscribe(r => result = r);
tick(300); // Simuliere 300ms Zeitverlauf
expect(result).toBe('results');
}));fakeAsync erstellt eine Zone mit virtueller Zeit.
tick() springt die Zeit vorwärts, ohne real zu warten. Dies
beschleunigt Tests mit Timeouts oder Debouncing drastisch.
Unit-Tests sollen die zu testende Unit isolieren. Dependencies werden durch Test-Doubles ersetzt – Objekte, die das Interface implementieren, aber kontrolliertes Verhalten zeigen. Jasmine’s Spy-System implementiert dies elegant.
Ein Spy auf einer existierenden Methode:
const userService = new UserService(httpClient);
spyOn(userService, 'getUser').and.returnValue(
of({ id: 123, name: 'John' })
);
component.loadUser(123);
expect(userService.getUser).toHaveBeenCalledWith(123);
expect(component.user.name).toBe('John');spyOn ersetzt die Methode durch einen Spy. Das
and.returnValue kontrolliert den Return-Wert. Der Test
verifiziert sowohl den Aufruf als auch das Ergebnis.
Verschiedene Spy-Konfigurationen für verschiedene Szenarien:
// Einfacher Return-Wert
spyOn(service, 'getData').and.returnValue(testData);
// Exception werfen
spyOn(service, 'failingMethod').and.throwError('Network error');
// Custom-Implementierung
spyOn(service, 'transform').and.callFake(input => input.toUpperCase());
// Original-Methode aufrufen (für Beobachtung ohne Ersetzung)
spyOn(service, 'logActivity').and.callThrough();Für komplette Mock-Objekte nutzt createSpyObj:
const httpSpy = jasmine.createSpyObj('HttpClient', ['get', 'post', 'put', 'delete']);
httpSpy.get.and.returnValue(of({ data: 'mocked' }));
httpSpy.post.and.returnValue(of({ success: true }));
const service = new UserService(httpSpy);Das Mock-Objekt implementiert das Interface mit Spies für jede Methode. Die Service-Instanz erhält den Mock statt des echten HttpClient. Der Test bleibt isoliert – keine echten HTTP-Requests.
Spy-Verifikation prüft Aufruf-Details:
expect(spy).toHaveBeenCalled();
expect(spy).toHaveBeenCalledTimes(3);
expect(spy).toHaveBeenCalledWith('arg1', 'arg2');
expect(spy).not.toHaveBeenCalledWith('wrongArg');
expect(spy).toHaveBeenCalledBefore(otherSpy);
// Zugriff auf Aufruf-Details
expect(spy.calls.count()).toBe(2);
expect(spy.calls.argsFor(0)).toEqual(['first', 'call']);
expect(spy.calls.mostRecent().args).toEqual(['last', 'call']);Diese Assertions verifizieren nicht nur dass eine Methode aufgerufen wurde, sondern wie oft, mit welchen Parametern und in welcher Reihenfolge.
Standard-Matchers decken Common Cases ab. Domain-spezifische Assertions profitieren von Custom Matchers, die Tests lesbarer und wiederverwendbarer machen:
beforeEach(() => {
jasmine.addMatchers({
toBeValidEmail: () => ({
compare: (actual: string) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const pass = emailRegex.test(actual);
return {
pass,
message: pass
? `Expected ${actual} not to be a valid email`
: `Expected ${actual} to be a valid email`
};
}
}),
toBeWithinRange: () => ({
compare: (actual: number, min: number, max: number) => {
const pass = actual >= min && actual <= max;
return {
pass,
message: pass
? `Expected ${actual} not to be within range ${min}-${max}`
: `Expected ${actual} to be within range ${min}-${max}, but was ${actual}`
};
}
})
});
});
it('should validate email addresses', () => {
expect('user@example.com').toBeValidEmail();
expect('invalid-email').not.toBeValidEmail();
});
it('should validate number ranges', () => {
expect(50).toBeWithinRange(0, 100);
expect(150).not.toBeWithinRange(0, 100);
});Custom Matchers kapseln komplexe Validierungslogik. Die Fehlermeldungen sind spezifisch und hilfreich. Tests werden deklarativer und ausdrucksstärker.
Während der Entwicklung ist es oft nützlich, nur spezifische Tests auszuführen. Jasmine’s Fokussierungs-Features beschleunigen den Feedback-Loop:
// Nur dieser Test läuft
fit('focused test', () => {
expect(true).toBe(true);
});
// Alle anderen Tests werden ignoriert
it('ignored test', () => {
expect(false).toBe(false); // wird übersprungen
});
// Nur Tests in dieser Suite laufen
fdescribe('focused suite', () => {
it('runs', () => {
expect(true).toBe(true);
});
it('also runs', () => {
expect(true).toBe(true);
});
});
// Suite überspringen
xdescribe('disabled suite', () => {
it('skipped', () => {
expect(false).toBe(false); // wird nicht ausgeführt
});
});
// Einzelnen Test überspringen
xit('disabled test', () => {
expect(false).toBe(false); // wird nicht ausgeführt
});Das f-Präfix (focus) aktiviert selektive Ausführung. Das
x-Präfix (exclude) deaktiviert Tests. Diese Markierungen
sollten temporär sein – committed Code sollte keine focused Tests
enthalten, da dies andere Tests verschweigt.
Jasmine definiert Tests, Karma führt sie aus. Diese Trennung ermöglicht Flexibilität – Jasmine-Tests können in verschiedenen Umgebungen laufen, Karma kann verschiedene Testing-Frameworks orchestrieren.
Karma’s Architektur basiert auf einem Webserver, der Test-Code und Anwendungscode in Browser lädt. Die Browser werden zu Clients, die mit dem Karma-Server kommunizieren. Der Server sammelt Ergebnisse und präsentiert sie dem Entwickler.
Der Workflow unterstützt Watch-Mode. Karma beobachtet Dateien, erkennt Änderungen und führt Tests automatisch erneut aus. Der Entwickler erhält sofortiges Feedback ohne manuelle Test-Ausführung.
Die Datei karma.conf.js konfiguriert jeden Aspekt des
Test-Runners:
module.exports = function(config) {
config.set({
basePath: '',
frameworks: ['jasmine', '@angular-devkit/build-angular'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('@angular-devkit/build-angular/plugins/karma')
],
client: {
clearContext: false,
jasmine: {
timeoutInterval: 10000,
random: false
}
},
jasmineHtmlReporter: {
suppressAll: false,
suppressFailed: false
},
coverageReporter: {
dir: require('path').join(__dirname, './coverage'),
subdir: '.',
reporters: [
{ type: 'html' },
{ type: 'text-summary' },
{ type: 'lcovonly' }
],
check: {
global: {
statements: 80,
branches: 75,
functions: 80,
lines: 80
}
}
},
reporters: ['progress', 'kjhtml', 'coverage'],
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
restartOnFileChange: true
});
};Die Konfiguration steuert Browser-Auswahl, Reporter-Ausgabe,
Coverage-Thresholds und mehr. Der frameworks-Array
definiert verwendete Testing-Frameworks. Der plugins-Array
registriert Karma-Plugins für Browser-Launcher, Reporter und
Build-Integration.
Karma unterstützt alle gängigen Browser. Die Wahl hängt vom Kontext ab – lokale Entwicklung, CI/CD-Pipeline, Cross-Browser-Testing:
// Lokale Entwicklung: Sichtbare Browser
browsers: ['Chrome'],
// CI/CD: Headless Browser
browsers: ['ChromeHeadless'],
// Cross-Browser-Testing
browsers: ['Chrome', 'Firefox', 'Safari'],
// Custom Launcher für spezielle Flags
customLaunchers: {
ChromeHeadlessCI: {
base: 'ChromeHeadless',
flags: [
'--no-sandbox',
'--disable-gpu',
'--disable-dev-shm-usage'
]
},
ChromeDebug: {
base: 'Chrome',
flags: ['--remote-debugging-port=9333']
}
},
browsers: process.env.CI ? ['ChromeHeadlessCI'] : ['Chrome']Die --no-sandbox-Flag ist oft nötig in Docker-Containern
oder CI-Umgebungen mit eingeschränkten Berechtigungen. Die
--remote-debugging-port-Flag ermöglicht externes Debugging
während der Test-Ausführung.
Code-Coverage misst, welcher Anteil des Codes durch Tests abgedeckt ist. Karma integriert Istanbul für Coverage-Tracking:
coverageReporter: {
dir: 'coverage/',
reporters: [
{ type: 'html', subdir: 'html' },
{ type: 'lcovonly', subdir: '.' },
{ type: 'text-summary' },
{ type: 'json', subdir: '.', file: 'coverage.json' }
],
check: {
global: {
statements: 80,
branches: 70,
functions: 80,
lines: 80
},
each: {
statements: 60,
branches: 50
}
}
}Die check-Konfiguration definiert minimale
Coverage-Prozentsätze. Unterschreiten Tests diese Thresholds, schlägt
der Build fehl. Dies erzwingt Mindest-Testabdeckung in
CI/CD-Pipelines.
Coverage-Typen haben unterschiedliche Bedeutungen:
| Metrik | Bedeutung | Interpretation |
|---|---|---|
| Statements | Ausgeführte Code-Zeilen | Grundlegende Abdeckung |
| Branches | If/else-Zweige durchlaufen | Edge Cases getestet |
| Functions | Aufgerufene Funktionen | API-Surface getestet |
| Lines | Physische Zeilen ausgeführt | Ähnlich Statements |
Hohe Coverage garantiert nicht gute Tests – sie misst nur Ausführung, nicht Qualität. Niedrige Coverage weist jedoch auf ungetestete Bereiche hin.
Die Angular CLI integriert Karma nahtlos. Standard-Befehle abstrahieren Karma-Details:
# Tests ausführen im Watch-Mode
ng test
# Einmalige Ausführung für CI/CD
ng test --watch=false --browsers=ChromeHeadless
# Mit Coverage-Report
ng test --code-coverage
# Spezifische Dateien testen
ng test --include='src/app/auth/**/*.spec.ts'
# Mit Source Maps für Debugging
ng test --source-mapDie CLI konfiguriert Karma automatisch basierend auf
angular.json. Custom-Konfiguration in
karma.conf.js überschreibt Defaults.
Ein Vorteil von Karma ist natives Browser-Debugging. Der Test-Runner öffnet einen Browser mit Debug-Interface:
ng test startet Karma und öffnet BrowserDie Debug-URL kann modifiziert werden für selektive Ausführung:
http://localhost:9876/debug.html?spec=UserService%20should%20authenticate
Dies lädt nur Tests, die “UserService should authenticate” matchen. Kombiniert mit Breakpoints ermöglicht dies fokussiertes Debugging komplexer Tests.
Karma ist CI/CD-ready. Die Konfiguration unterscheidet zwischen lokaler Entwicklung und CI-Umgebung:
module.exports = function(config) {
const isCI = process.env.CI === 'true';
config.set({
browsers: isCI ? ['ChromeHeadlessCI'] : ['Chrome'],
singleRun: isCI,
autoWatch: !isCI,
customLaunchers: {
ChromeHeadlessCI: {
base: 'ChromeHeadless',
flags: [
'--no-sandbox',
'--disable-gpu',
'--disable-dev-shm-usage',
'--disable-software-rasterizer',
'--disable-extensions'
]
}
},
browserDisconnectTimeout: 10000,
browserNoActivityTimeout: 60000,
captureTimeout: 60000
});
};Die Environment-Variable CI steuert das Verhalten. In CI
laufen Tests headless, einmalig, mit erhöhten Timeouts für langsamere
Umgebungen.
GitHub Actions Beispiel:
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node
uses: actions/setup-node@v3
with:
node-version: '18'
cache: 'npm'
- name: Install Dependencies
run: npm ci
- name: Run Tests
run: npm test -- --watch=false --browsers=ChromeHeadlessCI
- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage/lcov.infoDie Pipeline installiert Dependencies, führt Tests aus und uploaded
Coverage-Reports. Die --watch=false-Flag beendet Karma nach
Test-Completion.
Karma’s Abhängigkeit von echten Browsern führt zu plattformspezifischen Problemen. Typische Issues und Lösungen:
Browser startet nicht in CI:
customLaunchers: {
ChromeHeadlessCI: {
base: 'ChromeHeadless',
flags: [
'--no-sandbox',
'--disable-setuid-sandbox',
'--disable-gpu',
'--disable-dev-shm-usage'
]
}
}Docker-Container und CI-Umgebungen haben oft eingeschränkte
Sandbox-Permissions. Die --no-sandbox-Flag umgeht dies. Die
--disable-dev-shm-usage-Flag verhindert /dev/shm Memory
Issues in Containern.
Timeout-Fehler bei langsamen Tests:
client: {
jasmine: {
timeoutInterval: 30000 // 30 Sekunden
}
},
browserDisconnectTimeout: 20000,
browserNoActivityTimeout: 60000Langsame CI-Maschinen oder komplexe Tests benötigen höhere Timeouts. Zu hohe Werte verschleiern jedoch echte Probleme – Tests sollten schnell sein.
Tests laufen lokal, scheitern in CI:
logLevel: config.LOG_DEBUG,
browsers: process.env.CI ? ['ChromeHeadlessCI'] : ['Chrome'],
customLaunchers: {
ChromeHeadlessCI: {
base: 'ChromeHeadless',
flags: [
'--no-sandbox',
'--disable-gpu',
'--window-size=1920,1080'
]
}
}Debug-Logging offenbart oft Unterschiede. Die
--window-size-Flag stellt konsistente Viewport-Größen
sicher, wichtig für UI-Tests.
Jasmine und Karma sind Angular’s Defaults, aber nicht die einzigen Optionen. Jest gewinnt an Popularität durch Speed und Developer Experience:
ng add @angular-builders/jestJest kombiniert Test-Framework und Runner in einem Tool. Es läuft in Node.js statt echten Browsern, was drastisch schneller ist. Die Snapshot-Testing-Features vereinfachen UI-Regression-Tests.
Ein Jest-Test sieht Jasmine ähnlich:
describe('UserService', () => {
let service: UserService;
beforeEach(() => {
service = new UserService();
});
it('should create user', () => {
const user = service.createUser({ name: 'John' });
expect(user).toMatchSnapshot();
});
});Die Snapshot wird beim ersten Lauf gespeichert. Folgende Runs vergleichen gegen die Snapshot. Änderungen am Output werden als Failure markiert, bis die Snapshot aktualisiert wird.
Cypress erweitert sich von E2E zu Component Testing:
import { UserCardComponent } from './user-card.component';
describe('UserCardComponent', () => {
it('should display user information', () => {
cy.mount(UserCardComponent, {
componentProperties: {
user: { name: 'John', email: 'john@example.com' }
}
});
cy.get('.user-name').should('contain', 'John');
cy.get('.user-email').should('contain', 'john@example.com');
});
});Cypress rendert Komponenten in einem echten Browser, ähnlich Karma. Die API ist jedoch anders – Cypress verwendet Chaining statt Jasmine’s Expect-Syntax. Die Time-Travel-Debugging-Features sind mächtig für komplexe Interaktionen.
Die Wahl zwischen Tools hängt von Priorities ab. Jasmine/Karma ist batterie-included für Angular. Jest ist schneller und moderner. Cypress bietet besseres Debugging. Für die meisten Projekte bleibt der Standard die sichere Wahl – etabliert, gut dokumentiert, von der Angular CLI supportet.