Deinen ersten TestCase schreiben

Komponiere TestSteps zu einem Business-Flow-TestCase — die Naming- und Re-Export-Konventionen, denen eine gescaffoldete App folgt, und warum sie so aussehen.

← Zurück zur Übersicht · 🇬🇧 English · ← FAQ


Voraussetzungen: du hast deine App hinzugefügt (siehe App hinzufügen), ihre Screens als Controls modelliert (siehe Controls hinzufügen) und Tester-facing TestSteps komponiert (siehe TestSteps bauen). Diese Seite komponiert diese TestSteps zu einem TestCase.


TestCase-Struktur in einem Satz

Ein TestCase in pm/3_Cases/TC_*.spec.ts komponiert TestSteps, die aus pm/2_Apps/<AppName>/2_Steps/*.ts exportiert werden — nicht mehr. TestSteps selbst kommen aus einer von drei Quellen: einer Core.defineTestStep/Core.defineExecutionStep-Factory, einer handgeschriebenen Funktion gegen eine Control, oder einem Custom Step, den der Authoring-Agent generiert hat. Siehe Konzepte für das vollständige Drei-Schichten-Bild (Controls → TestSteps → TestCases), das diese Seite als bekannt voraussetzt.


Step-Naming-Konvention

Die kanonische Form für jeden TestStep, der aus einer gescaffoldeten App exportiert wird, ist:

TS_<Type>_<Action>
Beispiel Bedeutung
TS_Execution_Start App-Lifecycle-Step — siehe Core.defineExecutionStep
TS_Main_Button_Click Control-Interaktion auf der Main-Sicht
TS_Dialog_Textfield_Fill Control-Interaktion in einem Dialog
TS_Custom_MyCheck Handgeschriebener oder Agent-generierter Custom Step

Warum der App-Name nicht Teil des Step-Namens ist

  • Der Barrel-Namespace trägt ihn schon. Ein Step wird immer über das Barrel seiner App aufgerufen — Project.MyApp.TS_Execution_Start() — die App-Identität steckt an der Call-Site also schon drin. Sie im Funktionsnamen zu wiederholen würde nur Information duplizieren, die der Namespace bereits liefert.
  • Registration ist ein anderes Konzept als Naming. Das erste Argument von Core.defineTestStep('MyApp', ...) / Core.defineExecutionStep('MyApp', 'Start', ...) ist der Schlüssel der App in GlobalConfig.apps — es sagt dem Framework, aus welchem App-Eintrag page/url/executable/appiumUrl aufzulösen sind. Das hat nichts mit dem Namen der exportierten Funktion zu tun; beide sind unabhängig voneinander, und der Registration-Key bleibt exakt so stehen, obwohl er im Namen nicht mehr auftaucht.
  • Agent-Authoring-freundlich. Ein Authoring-Agent, der einen Step generiert oder repariert, liest Region/Element/Aktion direkt aus dem Funktionsnamen; zu welcher App der Step gehört, ist bereits eindeutig aus dem Barrel-Namespace, über den er exportiert wird.
  • Tester-Scanning-freundlich. Der Step-Picker eines Test-Management-Tools gruppiert Steps bereits über den Barrel-Namespace nach App — ein wiederholtes App-Namen-Segment in jedem Funktionsnamen würde der Liste nur Rauschen hinzufügen.

💡 Dieselbe Form wie die ältere Fixture-Konvention. Aus _Skeleton gescaffoldete Apps folgen durchgehend TS_<Type>_<Action>. Das ist dieselbe Konvention, die in Konzepte als TS_<Region>_<ElementType>_<Verb> beschrieben wird — TS_Main_Button_Click gliedert sich in Region Main, Element Button, Verb Click. <Type> oben entspricht <Region>_<ElementType>, <Action> dem <Verb>; beide Namen beschreiben dieselbe Konvention.

💡 Kommst du von v0.13.0? Step-Export-Namen enthielten früher den App-Namen (TS_MyApp_Execution_Start). Ab v0.14.0 lebt der App-Name nur noch im Barrel-Namespace — benenne einen bestehenden Export um, indem du sein App-Namen-Segment entfernst (TS_MyApp_Execution_StartTS_Execution_Start); die Call-Site funktioniert nach der Umbenennung weiter, da Project.MyApp. die App-Identität schon trug.

🛡️ Automatisch durchgesetzt. Das Framework validiert jeden TS_*-Export gegen diese Naming-Konvention beim Commit über einen Pre-Commit-Hook und in der CI. Siehe Skeleton-Konventionen für das vollständige Regelwerk (Naming, Signaturen, XPath-Style, i18n).


Re-Export-Konvention

Das 2_Apps/<AppName>/References.ts-Barrel einer App re-exportiert seine eigenen Ordner in zwei unterschiedlichen Formen — Steps flach, Controls namespaced:

// 2_Apps/<AppName>/References.ts

// Controls — namespaced
export * as ControlsButton from './1_Controls/Control_Button';
export * as ControlsTextfield from './1_Controls/Control_Textfield';

// Steps — flach
export * from './2_Steps/TS_Execution';
export * from './2_Steps/TS_Main';
export * from './2_Steps/TS_Dialog';

Warum Steps flach sind

Die App-Identität eines Steps ist schon durch den Barrel-Namespace festgelegt, über den er exportiert wird — Project.MyApp.TS_Execution_Start() — ein zusätzlicher Pro-Step-Namespace innerhalb desselben Barrels würde also nichts hinzufügen, was die Call-Site nicht schon weiss. Fixture-artige Apps in der eigenen Test-Suite dieses Frameworks re-exportieren ihre Steps von Anfang an flach; das gescaffoldete _Skeleton folgt demselben Muster.

Warum Controls namespaced bleiben

Controls nutzen generische, wiederholte Methodennamen (click, checkVisible, fill), die nur zusammen mit der Identität ihrer Control Sinn ergeben — ein flacher click(...)-Export würde kollidieren, sobald zwei Controls beide einen anbieten. Der Namespace (ControlsButton.click(...) vs. ControlsLink.click(...)) ist das, was sie unterscheidbar macht.


Dein erster TestCase

// 3_Cases/TC_MyFirstFlow.spec.ts
import * as Project from '@GlobalRef';

Project.Core.test('TC_MyFirstFlow', async () => {
    Project.Core.Step.setCurrentTestCaseName('TC_MyFirstFlow');

    await Project.Core.Step.numberedStepBlock('Ablauf ausführen', async () => {
        await Project.MyApp.TS_Execution_Start();
        await Project.MyApp.TS_Main_Button_Click('Save');
        await Project.MyApp.TS_Execution_Close();
    });
});

TS_Execution_Start/_Close nehmen überhaupt kein page- oder url-Argument — Core.defineExecutionStep löst beides intern aus dem passenden Eintrag in GlobalConfig.apps auf. Siehe API-Referenz — Sektion 16 für die vollständige env-Form, die diese Factory injiziert.

Ein Step, dessen Name mit Get/Read/List beginnt, gibt den ermittelten Wert zurück — weise ihn einer Variable zu und nutze ihn in einem Folgeschritt oder einer Prüfung weiter. Siehe TestSteps bauen — Einen Wert aus einem Get-/Read-Step liefern für das Muster.


Step-Blöcke: Gruppierung, Zeit-Limits und eigene Fehlermeldungen

Project.Core.Step.numberedStepBlock(titel, body, options?) fasst mehrere TS_*-Aufrufe zu einem benannten Block (TSB_x) zusammen — für die Report-Struktur und die Lesbarkeit. Die Dauer jedes Blocks wird automatisch gemessen und im Report als duration_ms festgehalten; dafür musst du nichts tun.

Das optionale dritte Argument options schaltet zwei zusätzliche Fähigkeiten frei. Beide sind opt-in — ohne options verhält sich der Block exakt wie bisher.

Zeit-Limits (durationLimits) — eine Eskalations-Leiter

Eine Liste von Schwellen, jede mit eigenem Level und eigener Meldung:

await Project.Core.Step.numberedStepBlock('Redirect zu AGOV prüfen', async () => {
    await Project.MyApp.TS_Execution_Browser_Start();
    await Project.MyApp.TS_Main_Label_ByClassName_IsEqual('start', '', 'labelQR', 'Scan the QR code');
}, {
    durationLimits: [
        { ms: 6000,  level: 'warn', message: 'Redirect ist langsam' },
        { ms: 10000, level: 'fail', message: 'Redirect zu langsam (SLA verletzt)' },
    ],
});
  • Ausgewertet wird nur bei Erfolg des Blocks (die Dauer eines gescheiterten Blocks ist aussagelos).
  • Es feuert genau die schwerste verletzte Schwelle (fail > warn > info; bei Gleichstand die mit dem größeren ms). Die Reihenfolge in der Liste spielt keine Rolle.
  • info (Default, wenn level weggelassen wird) und warn loggen nur — der Test bleibt grün. fail lässt den Block fehlschlagen (assertion-artig, integriert sich also in den Run-Mode: soft in failsafe, hart in failfast).
  • message ist optional; ohne fällt eine generische „took X ms (over the Y ms limit)”-Zeile ein.
  • Ist eine Schwelle verletzt, stehen im Report zusätzlich limit_ms, limitLevel und limitExceeded (sonst erscheinen diese Felder gar nicht).

Eigene Fehlermeldung (failureMessage)

Ein fach-lesbarer Text, der bei einem funktionalen Fehler des Blocks vorangestellt wird — ohne die eigentliche Ursache zu verwerfen:

await Project.Core.Step.numberedStepBlock('Redirect zu AGOV prüfen', async () => {
    await Project.MyApp.TS_Execution_Browser_Start();
    await Project.MyApp.TS_Main_Label_ByClassName_IsEqual('start', '', 'labelQR', 'Scan the QR code');
}, {
    failureMessage: 'Redirect zu AGOV ist fehlgeschlagen',
});

Schlägt ein Schritt im Block fehl, lautet die Meldung z. B. Redirect zu AGOV ist fehlgeschlagen: <ursprünglicher Fehler>. Der Original-Fehler (inklusive Stack und Assertion-Form) bleibt erhalten — das ersetzt das Anti-Pattern catch { throw new Error('...') }, das die Ursache wegwerfen würde. Bei einem fail aus durationLimits wird failureMessage nicht zusätzlich vorangestellt (die Schwelle trägt ihre eigene Meldung).

Beide Optionen sind ab Framework 0.48.0 verfügbar.


Gegen die mitgelieferte Demo ausprobieren

Statt vor deinem ersten Lauf eigene Controls und TestSteps zu schreiben, kannst du einen TestCase stattdessen gegen die mitgelieferte Demo Web App des Frameworks richten — eine kleine ERP-artige App mit passendem, sofort nutzbarem Preset:

  1. npm install --save-dev @meintest/cc-testframework-demo-web (aus pm/ heraus)
  2. Das _ExampleWebApp-Preset aus den Templates neben deinem Projekt scaffolden (Controls, TestSteps und ihre .i18n.json-Kataloge kommen vorinstanziiert mit — nichts zu schreiben)
  3. Den mitgelieferten TC_ExampleWebApp_HappyPath.spec.ts laufen lassen — ein vollständiger Login-→-CRUD-→-Logout-Ablauf, komponiert genau so, wie Dein erster TestCase oben es beschreibt

Siehe Demo Web App für das vollständige Setup (inklusive des GlobalConfig.apps-Eintrags) und eine Feature-Tour.

Für Datei-Operationen, Registry-Zugriffe, System-Zeit-Checks oder beliebige Shell-Kommandos liefert das Framework zwei gebrauchsfertige Apps mit — siehe OS-App.


Wo es weitergeht

  • Konzepte — die vollständige Drei-Schichten-Architektur (Core / 2_Apps / 3_Cases), die diese Seite voraussetzt
  • Skeleton-Konventionen — die 6 Regeln, die das Framework auf jeder gescaffoldeten App durchsetzt (Naming, Signatur, XPath, i18n), und wie du über strictConventions die volle Durchsetzung auf deinen eigenen Apps aktivierst
  • API-Referenz — Sektion 16 — die Signatur von Core.defineExecutionStep und ihre family-aware env-Form
  • Custom Steps — die @custom-Konvention für einen Step, für den es noch keine bestehende Control-×-Action-Kombination gibt
  • Step-Description Localization — die Beschreibung eines Steps in der Sprache eines Testers zeigen, über einen Sibling-.i18n.json-Katalog
  • Demo Web App — eine mitgelieferte Beispiel-App und ein TestCase zum Ausprobieren, bevor du deine eigene verdrahtest

Nächster Schritt: Run and Debug — diesen TestCase ausführen und bei Fehlschlägen iterieren.


📧 Fragen? Kontakt: jens.szelag@itsbusiness.ch

itsbusiness AG · Bern · Schweiz