Durchsuchbare Dokumentation aufrufen | Zurück zur Dokumentationsübersicht
Navigation: Dokumentationen agorum core > agorum core für Entwickler
Das Modul agorum.dev enthält eine Testsuite für die Durchführung von automatisierten Tests, etwa für Workflows, Aktive Ordner und mehr. Eine testgetriebene Entwicklung (test driven development) verbessert die Qualität von Code.
Mithilfe des Moduls können Sie:
Einen Test definieren Sie über ein Test-Skript auf eine von zwei Arten:
Eigene Dateien/Administration/customers/<Konfigurationsprojekt>
Hinweis: Sie können das Skript an dieser Stelle nicht starten, sondern nur im Rahmen der Testsuite.
Eigene Dateien/Administration/customers/<Konfigurationsprojekt>/test/js
Erstellen Sie einen Test über die Kopfleiste, müssen Sie das Konfigurationsprojekt manuell wählen.
Hinweis: Sie können das Skript an dieser Stelle nicht starten, sondern nur im Rahmen der Testsuite.
Eigene Dateien/Administration/customers/<Konfigurationsprojekt>/test/js
Sie führen einen Test über die Testsuite aus, um den Test aufzurufen. Die Testsuite öffnen Sie in Ihrem Konfigurationsprojekt.
Eigene Dateien/Administration/customers/<Konfigurationsprojekt>
Tipp: Klicken Sie innerhalb Ihres Konfigurationsprojekts ein bereits abgelegtes Test-Skript oder einen Ordner mit Test-Skripts mit der rechten Maustaste an, öffnen Sie die Testsuite nur für dieses eine Skript oder für die Skripte, die sich in diesem Ordner befinden.
Sie können innerhalb der Testsuite in der Baumstruktur einen Test direkt bearbeiten.
Sie können einen Test für einen bereits gelaufenen Workflow automatisch erzeugen, um einen Workflow auf Korrektheit zu prüfen.
Hinweis: Das Skript enthält die JavaScript-Bibliotheken:
• common/objects
• common/beans
• agorum.dev/js/lib/delta
• agorum.dev/js/lib/workflow
• agorum.dev/js/lib/assert
• agorum.dev/js/lib/finder
• agorum.dev/js/lib/cleaner
Eigene Dateien/Administration/customers/<Konfigurationsprojekt>/test/js
Hinweise:
• Das System führt den Test stets mit dem Test-Anhang test.pdf durch, wenn Sie das gespeicherte Test-Skript starten.
• Sie müssen den Test-Anhang test.pdf sowie den Ordner pdf manuell erstellen unter:
Eigene Dateien/Administration/customers/<Konfigurationsprojekt>/test/pdf
Für manche Tests benötigen Sie Metadaten, damit Sie Tests vollständig ausführen können. Die Metadaten eines Objekts können Sie über das Kontextmenü auslesen.
Sie können über die Auswahlliste What festlegen, welche Metadaten angezeigt werden sollen:
| Option | Beschreibung |
|---|---|
| not inherited | Zeigt nur die nicht vererbten/vererbbaren Metadaten an (Marker ~).Diese Metadaten sind direkt am Objekt gespeichert und werden nicht an untergeordnete Objekte vererbt. |
| own | Zeigt alle 'eigenen' Metadaten an (Marker ~ und ~~).Dazu gehören sowohl nicht vererbbare Metadaten als auch Metadaten, die das Objekt an untergeordnete Objekte vererbt. |
| all | Zeigt alle Metadaten an, einschließlich geerbter Metadaten von übergeordneten Objekten. |
Der Metadata Reader bietet zwei Möglichkeiten, die angezeigten Metadaten zu kopieren:
Kopiert nur die Metadaten-Daten als JavaScript-Objekt in die Zwischenablage.
Beispiel:
{
ag_identifier: "test-ordner",
ag_area: ["Kunden", "Projekte"],
custom_field: "Wert"
}
Diese Option eignet sich, wenn Sie die Daten direkt in Ihrem Code verwenden möchten.
Kopiert die Metadaten als vollständige JavaScript-Variable-Deklaration in die Zwischenablage.
Beispiel:
let data = {
ag_identifier: "test-ordner",
ag_area: ["Kunden", "Projekte"],
custom_field: "Wert"
};
Diese Option eignet sich besonders für Test-Skripte, da die Variable direkt verwendet werden kann.
Die kopierten Metadaten können Sie in Test-Skripten verwenden, um:
Der Dialog Objekteigenschaften kopieren zeigt wichtige technische Informationen zu einem ausgewählten agorum core-Objekt an und ermöglicht das einfache Kopieren dieser Werte in die Zwischenablage.
Der Dialog zeigt, abhängig vom gewählten Objekt, verschiedene Objekteigenschaften an:
| Eigenschaft | Beschreibung |
|---|---|
| Name | Der vollständige Name des Objekts, einschließlich Dateiendung (falls vorhanden). |
| UUID | Die eindeutige UUID (Universally Unique Identifier) des Objekts. Diese ändert sich nie und identifiziert das Objekt eindeutig. |
| ID | Die numerische interne ID des Objekts. Diese ist eindeutig innerhalb einer agorum core Instanz. |
| Paths | Alle Ordnerpfade, unter denen das Objekt zu finden ist. Dies umfasst sowohl den Hauptablageort als auch alle Verknüpfungen (Links). Sortierung: Die Pfade werden aufsteigend nach Länge sortiert angezeigt (kürzeste zuerst). |
| MetaDB | Alle MetaDB-Pfade, die mit dem Objekt verknüpft sind. Das System zeigt sowohl direkte MetaDB-Pfade als auch Pfade, die über Gruppen-Hierarchien entstehen. Was wird angezeigt:
Sortierung: Die MetaDB-Pfade werden aufsteigend nach Länge sortiert angezeigt (kürzeste zuerst). |
| Require | Fertige require()-Statements für JavaScript-Dateien. Diese Kategorie wird nur angezeigt, wenn das Objekt eine JavaScript-Datei ist oder ein index.js enthält. Eigenschaften:
|
Neben jedem Wert befindet sich ein Kopieren-Symbol .
/* globals describe, it, before, after, beforeEach, afterEach */
let beans = require('common/beans');
let objects = require('common/objects');
let assert = require('/agorum/roi/customers/agorum.dev/js/lib/assert');
let cleaner = require('/agorum/roi/customers/agorum.dev/js/lib/cleaner');
// desribe a group of tests
describe('test group', () => {
/** @type {agorum.FolderObject} */
let resources;
/** @type {agorum.FolderObject} */
let workspace;
/** @type {cleaner.Cleaner} */
let c;
before('before', () => {
// look up this test's resources and workspace folders
let pkg = beans.up(objects.find(module.id), '[~identifier=ag_package]');
resources = pkg.getItem('test');
workspace = objects.find('agorum/roi/workspace').createPath(pkg.name + '/test');
c = cleaner();
});
after('after', () => {
// clean up created objects
c.clean();
});
beforeEach('before each', () => {
// is called before each "it"
});
afterEach('after each', () => {
// is called after each "it"
});
// define your first test
it('test 1', cx => {
// Put in your test
// wait for anything
let result = cx
.wait('Describe, what you are waiting for')
.seconds(10)
.for(() => {
// return what you found
return 'anything';
});
// define checkpoints, that are shown as progress in the test suite
cx.checkpoint('Checkpoint 1');
// assert the result (should be "anything")
assert('Check with isEqual').isEqual(result, 'anything');
});
// define your second test
it('test 2', () => {
// ...
// if the test returns anything, it is marked as failed
// you can also throw an error to mark it as failed
});
});
Definiert eine Test-Gruppe.
Definiert den Test an sich und führt ein oder mehrere Tests durch.
it('Test mit promise', cx => {
return new Promise(resolve => {
// ... etwas Asynchrones tun ...
resolve();
});
});
// oder
it('Test mit fork', cx => { // "fork" in der Regel nicht notwendig, da die Tests automatisch in eigenen Threads laufen
return aguila.fork(() => {
// ... etwas Asynchrones tun ...
});
});
Ignoriert alle anderen normalen decribes, sofern mindestens ein describe.only (statt describe) existiert.
Das System betrachtet beim Ausführen der Tests nur noch diejenigen Gruppen, die only sind.
Ignoriert alle anderen normalen it, sofern mindestens ein it.only (statt it) existiert, und führt nur die it.only aus.
Ignoriert die aufgeführte Gruppe.
Ignoriert den aufgeführten Test.
Wird ausgeführt vor dem ersten it innerhalb dieser Gruppe.
Verwenden Sie before, um Testdaten vorzubereiten.
Beispiel:
before('before', () => {
// is called before the first "it"
});
Wird ausgeführt nach dem letzten it innerhalb dieser Gruppe.
Verwenden Sie after, um Testdaten aufzuräumen.
Beispiel:
after('after', () => {
// is called after the last "it"
// may be used to clean up
});
Wird vor jedem einzelnen it innerhalb dieser Gruppe ausgeführt.
Beispiel:
beforeEach('before each', () => {
// is called before each "it"
});
Wird nach jedem einzelnen it innerhalb dieser Gruppe ausgeführt.
Beispiel:
afterEach('after each', () => {
// is called after each "it"
});
Stellt einen Kontext innerhalb des Tests dar und kann verwendet werden für:
Setzt einen Checkpoint.
Wartet auf etwas.
cx.wait('Ein sinnvoller Text, worauf hier gewartet wird').seconds(Anzahl Sekunden).for(<Funktion, die im Erfolgsfall etwas zurückliefert>);
Sie können anstatt seconds auch minutes verwenden:
Das System:
Hinweis: Sobald der return-Wert einer truthy-Antwort (NICHT: undefined, false, '') entspricht, wird der wait abgebrochen und das Ergebnis weitergeleitet.
Beispiel 1
Warte maximal 120 Sekunden auf das Erscheinen einer bestimmten Datei in einem Ordner und gib diese Datei zurück:
let folder = objects.find('/agorum/roi/Files/xy');
let file = cx.wait('Datei test.txt in Ordner xy enthalten?').seconds(120).for(() => {
return folder.getItem('test.txt');
});
// mit file weiterarbeiten
Beispiel 2
Warte maximal 120 Sekunden, bis ein bestimmtes Objekt in der Suche gefunden wurde, und gib die UUID zurück:
let uuid = cx.wait('Finde Objekt mit prefix_kennung=123').seconds(120).for(() => {
let result = objects.query('prefix_kennung:123').limit(1).search('uuid');
if (result && result[0]) return result[0].uuid;
});
// mit der uuid weiterarbeiten
Stellt eine Hilfsfunktion zur Prüfung von erwarteten Ergebnissen dar (siehe JavaScript-Bibliothek agorum.dev/js/lib/assert).
Automatisierte Tests für aguila-Widgets erfordern einen besonderen Umgang, da Autotests außerhalb des aguila-UI-Threads laufen, Widget-Zugriffe aber nur innerhalb des UI-Threads erlaubt sind. Die Funktion aguila.invoke() löst dieses Problem: Sie führt eine Funktion synchron im UI-Thread aus und gibt den Rückgabewert zurück.
Für Widget-Tests werden folgende Imports benötigt:
let assert = require('/agorum/roi/customers/agorum.dev/js/lib/assert');
let aguila = require('common/aguila');
let objects = require('common/objects');
Laden Sie das zu testende Widget mit require.exec() innerhalb von aguila.invoke().
Tipp: Testen Sie immer das echte Widget – bauen Sie den Widget-Code nicht im Test nach.
let testForm;
before('setup', () => {
testForm = aguila.invoke(() => {
// Das echte Widget laden – NICHT den Code im Test duplizieren!
let w = require.exec(
objects.find(require.resolve('/agorum/roi/customers/<Konfigurationsprojekt>/js/aguila/<widget-name>'))
);
w.popup({});
return w;
});
});
after('cleanup', () => {
aguila.invoke(() => {
testForm && testForm.form && testForm.form.close && testForm.form.close();
});
});
Hinweis: require.exec() ist die bevorzugte Methode für Tests, da sie keine Widget-Registrierung erfordert. Der Aufruf muss innerhalb von invoke() erfolgen.
Jeder Widget-Zugriff (lesen, schreiben, Events auslösen) muss in einem eigenen aguila.invoke()-Block stattfinden:
it('should have correct value', () => {
// Wert setzen
aguila.invoke(() => {
testForm.set('fieldName.value', 42);
});
// Wert lesen und prüfen
let result = aguila.invoke(() => {
return testForm.get('fieldName.value');
});
assert('Wert sollte 42 sein').isEqual(42, result);
});
Verwenden Sie fire('input', ...), um echte Benutzereingaben zu simulieren. Das ist besser als nur Werte zu setzen, da so der gleiche Code-Pfad wie bei einer echten Benutzereingabe durchlaufen wird:
it('should react to user input', () => {
aguila.invoke(() => {
// Erst Werte setzen
testForm.set('celsius.value', 100);
// Dann input-Event feuern, um die Berechnung auszulösen
testForm.fire('input', { name: 'celsius', value: 100, oldValue: 0 });
});
let result = aguila.invoke(() => {
return testForm.get('fahrenheit.value');
});
assert('100°C sollten 212°F sein').isEqual(212, result);
});
it('should have all required fields', () => {
let fields = aguila.invoke(() => {
return [
testForm.down('meters') !== null && testForm.down('meters') !== undefined,
testForm.down('kilometers') !== null && testForm.down('kilometers') !== undefined,
];
});
assert('meters field should exist').isTrue(fields[0]);
assert('kilometers field should exist').isTrue(fields[1]);
});
Trennen Sie Geschäftslogik von der Oberfläche und testen Sie beides separat:
| Testtyp | Was wird getestet |
|---|---|
| Lib-Test | Reine Logik (Berechnungen, Konvertierungen) ohne aguila |
| UI-Test | Widget-Verhalten, Benutzerinteraktion mit aguila.invoke(), testet die Oberfläche |
Fehler in der Berechnungslogik werden auf diese Weise sofort gefunden, ohne dass die Widget-Erzeugung funktionieren muss. Die UI-Tests prüfen dann nur noch, ob die Oberfläche die Bibliothek korrekt einbindet.
Überprüfen, ob eine Datei im korrekten Ordner abgelegt wurde:
assert('Ist korrekt abgelegt').isTruthy(objects.tryFind('</erwarteter Pfad/erwarteter Dateiname.Dateiendung>'));
Überprüfen, ob eine Datei im korrekten Ordner abgelegt wurde (mit Verweis auf den Objektnamen):
assert('Ist korrekt abgelegt').isTruthy(objects.tryFind('</erwarteter Pfad/'> + obj.name));
Siehe E-Mail-Autotests
let assert = require('/agorum/roi/customers/agorum.dev/js/lib/assert');
let aguila = require('common/aguila');
let objects = require('common/objects');
describe('distance converter UI tests', () => {
let testForm;
before('setup', () => {
testForm = aguila.invoke(() => {
let w = require.exec(
objects.find(require.resolve('/agorum/roi/customers/<Konfigurationsprojekt>/js/aguila/<example>'))
);
w.popup({});
return w;
});
});
after('cleanup', () => {
aguila.invoke(() => {
testForm && testForm.form && testForm.form.close && testForm.form.close();
});
});
it('should update all fields when user enters 1000 meters', () => {
aguila.invoke(() => {
testForm.set('meters.value', 1000);
testForm.fire('input', { name: 'meters', value: 1000, oldValue: 0 });
});
let km = aguila.invoke(() => {
return testForm.get('kilometers.value');
});
assert('kilometers should be 1').isEqual(1, km);
});
});
Siehe JavaScript-Bibliothek agorum.dev/js/lib/runner