Softwaretest

Aus Zweites Gehirn, dem persönlichen Wiki
Softwaretest
TypKonzept
QuellenQuelle - Angular Mastery Workshop
Quelle - Recherche - Angular heute 2026
Quelle - Praxisbuch Usability und UX
Erstellt2026-09-26
Aktualisiert2026-09-27
Tagssoftwaretest, qualität, angular, softwareentwicklung

Automatisierte Tests prüfen, ob Code tut, was er soll. Mit guter Testabdeckung kann man Code mit Zuversicht ändern und erweitern. Unterschieden werden Unit-, Integrations- und End-to-End-Tests. In Angular heisst das vor allem: Service-Tests, Komponententests mit TestBed und E2E-Tests der wichtigsten Abläufe.

Code-Beispiele aus Quelle - Angular Mastery Workshop (Folie 250–255), von den Folienbildern abgeschrieben (Stand Angular 12).

Warum testen

„Gut getestete Anwendungen sind leicht zu korrigieren und zu erweitern.“ Mit guter Testabdeckung kann man den Code mit Zuversicht ändern (Quelle - Angular Mastery Workshop, Folie 245).

Testebenen

Ebene Gegenstand
Unit-Test einzelne Services, Pipes, Funktionen
Integrationstest Komponenten und Direktiven mit ihrem Template
End-to-End-Test (E2E) die ganze laufende Anwendung, ganze Geschäftsabläufe

Praktische Einteilung für Angular (Folie 246–247):

  • Service-Tests sind am einfachsten: Service erzeugen, Abhängigkeiten durch Attrappen ersetzen, Methoden prüfen. Ohne Abhängigkeiten reicht new MeinService() (Folie 250).
  • Komponententests sind aufwendiger, weil DOM und Ereignisse mitspielen. Obwohl oft „Komponenten-Unit-Tests“ genannt, sind sie technisch Integrationstests: TestBed startet eine kleine Angular-App mit allen nötigen Deklarationen und Providern (Folie 251–254).
  • E2E-Tests sind anfällig, aber für die wichtigsten Abläufe lohnen sie sich immer (Folie 247).

Service-Test

Ein Service ohne Abhängigkeiten lässt sich ohne Angular testen: Instanz von Hand erzeugen, Methode aufrufen (Folie 250):

@Injectable({
    providedIn: 'root'
})
export class OrderService {

    constructor() {}                // keine Abhängigkeiten

    calculateTotalOrderValue(orders: Order[]): number {
        return orders.reduce((total, order) => total + order.value, 0);
    }
}
import { OrderService } from './order.service';

const MOCK_ORDERS = [{ value: 100 }, { value: 200 }];

describe('OrderService', () => {
  let service: OrderService

  beforeEach(() => {
      service = new OrderService();
  });

  it('calculates total', () => {
    const total = service.calculateTotalOrderValue(MOCK_ORDERS);
    expect(total).toBe(300);
  });
});

Komponententest mit TestBed

Die zu testende Benachrichtigungs-Komponente zeigt den Zustand aus einem Service an und entfernt einen Eintrag bei Klick (Folie 252–253):

@Component({
    selector: 'my-org-notification',
    template: `
        <mat-card *ngFor="let notification of notifications | async" [ngClass]="notification.type">
            <mat-icon>{{ notification.type }}</mat-icon>
            <p>{{ notification.message }}</p>
            <button (click)="removeNotification(notification)"><mat-icon>clear</mat-icon></button>
        </mat-card>
    `,
})
export class NotificationComponent implements OnInit {

    notifications: Observable<Notification[]>;

    constructor(private notificationService: ReactiveNotificationService) {}

    ngOnInit() {
        this.notifications = this.notificationService.notifications;
    }

    removeNotification(notification: Notification) {
        this.notificationService.remove(notification);
    }
}

TestBed ist im Grunde ein NgModule wie AppModule oder CoreModule. Es startet eine kleine Angular-App und braucht dafür alle Deklarationen und Provider. Danach holt man sich fixture und Komponenteninstanz (Folie 254):

describe('HeaderComponent', () => {

  let component: HeaderComponent;

  let fixture: ComponentFixture<HeaderComponent>;

  beforeEach(async(() => {

    TestBed.configureTestingModule({
      imports: [RouterTestingModule, SharedModule],
      declarations: [HeaderComponent, NotificationComponent],
    }).compileComponents();

  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(HeaderComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });
});

Mocking

Hängt eine Komponente von Services ab, ersetzt man diese durch Attrappen (Mocks) und meldet sie per Dependency Injection anstelle des Originals an. Eine Hilfsfunktion durchsucht das erzeugte DOM (Folie 255):

const MOCK_NOTIFICATIONS: Notification[] = [{ id: 'abc', type: 'info', message: 'info message' }];

describe('NotificationComponent', () => {
  let component: NotificationComponent;
  let fixture: ComponentFixture<NotificationComponent>;
  let mockNotificationsService: Partial<NotificationService>;

  const getNotifications = () => fixture.debugElement.queryAll(By.css('.notification-item'));

  beforeEach(async(() => {
    mockNotificationsService = {
      notifications: of(MOCK_NOTIFICATIONS),
    };

    TestBed.configureTestingModule({
      imports: [NoopAnimationsModule, SharedModule],
      declarations: [NotificationComponent],
      providers: [
        {
          provide: ReactiveNotificationService,
          useValue: mockNotificationsService,
        },
      ],
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(NotificationComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should render notifications', () => {
    expect(getNotifications().length).toBe(2);   // will fail, why?
  });
});

Die Folie stellt die Frage „will fail, why?“ als Übung. Auflösung (Einordnung Claude): Der Mock liefert nur eine Benachrichtigung, also müsste toBe(1) stehen. Ausserdem sucht der Test nach .notification-item, das Template der Komponente setzt diese Klasse aber nicht, sondern nur notification.type. Mit der Komponente von Folie 253 findet der Test deshalb 0 Elemente.

Den HttpClient ersetzt HttpClientTestingModule (Code in Angular-Backend-Kommunikation). Streams testet man mit dem TestScheduler in Marble-Syntax, dann fällt die echte Zeit weg (Code in RxJS).

Werkzeuge

Widerspruch

Kurs: Karma/Jasmine ist der Standard für Unit-Tests, wird aber ab etwa 500 Tests langsam, Jest ist schneller. Für E2E ist Protractor Standard, Cypress eine Alternative, laut Kurs aber nur für Chromium-Browser (Folie 248–249). Heute: Seit v21 ist Vitest der Standard-Testrunner, Karma bleibt optional. Protractor ist seit 2021 veraltet und eingestellt (Quelle - Recherche - Angular heute 2026).

Einordnung (Claude)

Die Aussage, Cypress unterstütze nur Chromium, war schon 2021 überholt: Firefox wird seit 2020 unterstützt (nicht eigens recherchiert). Für E2E wird heute oft auch Playwright verwendet.

Einordnung (Claude)

Tests sind die Entwicklerseite der Qualitätssicherung, die im IT-Audit als Ergebnisprüfung auftaucht: Ein Test prüft das Ergebnis, ein Audit eher das Verfahren. Beides zusammen gibt Vertrauen in ein System.

Abgrenzung: Usability-Test

Die Tests oben prüfen, ob der Code richtig funktioniert. Ob Menschen die Anwendung bedienen können, prüft der Usability-Test: Testpersonen lösen Aufgaben, während man sie beobachtet. Fünf Personen finden rund 85 % der Bedienprobleme (Quelle - Praxisbuch Usability und UX, S. 189). In agilen Projekten lassen sich beide am Ende jedes Sprints verbinden (Nutzerzentrierte Entwicklung).

Verwandt