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 inGlobalConfig.apps— es sagt dem Framework, aus welchem App-Eintragpage/url/executable/appiumUrlaufzulö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
_Skeletongescaffoldete Apps folgen durchgehendTS_<Type>_<Action>. Das ist dieselbe Konvention, die in Konzepte alsTS_<Region>_<ElementType>_<Verb>beschrieben wird —TS_Main_Button_Clickgliedert sich in RegionMain, ElementButton, VerbClick.<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_Start→TS_Execution_Start); die Call-Site funktioniert nach der Umbenennung weiter, daProject.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ößerenms). Die Reihenfolge in der Liste spielt keine Rolle. info(Default, wennlevelweggelassen wird) undwarnloggen nur — der Test bleibt grün.faillässt den Block fehlschlagen (assertion-artig, integriert sich also in den Run-Mode: soft infailsafe, hart infailfast).messageist 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,limitLevelundlimitExceeded(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:
npm install --save-dev @meintest/cc-testframework-demo-web(auspm/heraus)- Das
_ExampleWebApp-Preset aus den Templates neben deinem Projekt scaffolden (Controls, TestSteps und ihre.i18n.json-Kataloge kommen vorinstanziiert mit — nichts zu schreiben) - Den mitgelieferten
TC_ExampleWebApp_HappyPath.spec.tslaufen 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
strictConventionsdie volle Durchsetzung auf deinen eigenen Apps aktivierst - API-Referenz — Sektion 16 — die Signatur von
Core.defineExecutionStepund ihre family-awareenv-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