Vorgehen: Eine Anwendung testen
Eine Access-Anwendung mit Tests prüfen, die in VBA geschrieben sind, und sie von der Befehlszeile oder einem CI-Server aus ausführen – ohne dass jemand am Desktop tippt oder klickt.
Lösungsüberblick
- Die Projektdatei nennt eine Testfunktion (
<Test>), die die Namen der Testprozeduren zurückgibt. - Eine Testprozedur ist ein öffentliches
Subohne Argumente in einem Standardmodul der Quellen. Sie steuert die Anwendung im Code: Sie öffnet Formulare mitDoCmd.OpenForm, ruft die Befehle der Anwendung auf, liest Steuerelemente und Daten und prüft die Ergebnisse mitWklTest.Check. - Die Anwendung lässt die Tests ihre Dialoge beantworten: Vor jedem
MsgBox,InputBoxoder Dateidialog fragt sieWklTest.Answer. Außerhalb eines Testlaufs ist die Antwort Empty, und der Dialog erscheint wie bisher. wkl-office testbaut das Projekt und führt dann jede Testprozedur einzeln in einem verborgenen Access aus, in einer Kopie der Datenbank. Es gibt eine Zeile je Prüfung aus und endet mit einem Exitcode; im Arbeitsordner liegt ein JUnit-Bericht.- PowerShell startet und wertet aus, VBA steuert. Ein Skript oder CI-Server ruft
wkl-office testundwkl-office callauf und liest Exitcode, JSON-Ausgabe und Berichte. Eine Schnittstelle, mit der ein Skript ein laufendes Access über COM steuert (Formular öffnen, Befehl auslösen, Steuerelement lesen), gibt es nicht; das tun die Testprozeduren in VBA.
Das SDK fügt das Modul WklTest in jeden Build eines Projekts ein, das eine Testfunktion nennt. Die Anwendung erhält
keinen neuen Verweis.
Umsetzung
1. Die Testfunktion in der Projektdatei nennen.
<Project> <PropertyGroup> <SchemaVersion>2</SchemaVersion> <AssemblyName>Orders</AssemblyName> <Test>AllTests</Test> </PropertyGroup> </Project>
2. Die Anwendung über WklTest.Answer fragen lassen. Eine Funktion je Dialogart genügt; der Rest der Anwendung
ruft sie statt MsgBox auf:
' A question to the user; in a test run the answer the test expects instead of the message box.
Public Function AskUser(ByVal Prompt As String, ByVal Buttons As VbMsgBoxStyle) As VbMsgBoxResult
Dim reply As Variant
reply = WklTest.Answer(WklTest.KindMessage, Prompt)
If IsEmpty(reply) Then
AskUser = MsgBox(Prompt, Buttons)
ElseIf IsNumeric(reply) Then
AskUser = CLng(reply)
Else
AskUser = vbCancel
End If
End Function
3. Die Testprozeduren schreiben, in einem Standardmodul der Quellen, zum Beispiel source\modules\basTests.bas:
Option Compare Database
Option Explicit
' The test procedures wkl-office test runs, in this order.
Public Function AllTests() As Variant
AllTests = Array("Orders_Total", "Orders_DeleteAsks")
End Function
Public Sub Orders_Total()
WklTest.Check "total of two lines", OrderTotal(Array(10, 5)) = 15, "total was " & OrderTotal(Array(10, 5))
End Sub
Public Sub Orders_DeleteAsks()
WklTest.Expect WklTest.KindMessage, "Delete order *", vbNo
DeleteOrder 42
WklTest.Check "order 42 kept after No", DCount("*", "tblOrder", "OrderId = 42") = 1, "order 42 was deleted"
End Sub
Eine Testprozedur braucht keine Fehlerbehandlung: Ein Laufzeitfehler, den sie nicht behandelt, wird als fehlgeschlagene Zeile mit dem Text des Fehlers gemeldet, und die nächste Prozedur läuft.
4. Die Tests ausführen.
wkl-office test Orders.acproj wkl-office test Orders.acproj --only Orders_DeleteAsks wkl-office test Orders.acproj -c Release -t accde --json
OK Orders_Total: total of two lines FAIL Orders_DeleteAsks: order 42 kept after No :: order 42 was deleted 1 OK, 1 FAIL (2 Testprozeduren)
Exitcode 0 bedeutet, dass jede Prüfung bestanden wurde, 15, dass eine Prüfung fehlschlug. Die übrigen Codes und die JSON-Ausgabe stehen in der Befehlszeilenreferenz.
Ist die Datenbank in Access geöffnet, kann der Build sie nicht ersetzen; --no-build testet dann die zuletzt gebaute
Datenbank, hier im Terminal des Add-Ins:
Ein Testlauf mit zwei Testprozeduren: eine Zeile je Prüfung, dann die Zusammenfassung.
Im VBA-Editor führt das Menü Tests des Add-Ins dieselben Testprozeduren auf der in Access geöffneten Datenbank aus, ohne den Editor zu verlassen; eine fehlgeschlagene Prüfung öffnet ihre Testprozedur aus dem Bereich Probleme (siehe Access-Add-In verwenden).
Aus PowerShell und CI ausführen
Mit --json gibt wkl-office test das Ergebnis als ein JSON-Objekt aus (Felder:
JSON-Ausgabe); der Exitcode sagt, wie der Lauf endete. Das folgende Skript
ist ein CI-Schritt im Projektordner: Es testet die ACCDB und die ACCDE, legt vor jedem Lauf die Eingabedateien aus
fixtures in den Arbeitsordner, gibt die fehlgeschlagenen Prüfungen aus und endet mit dem ersten Exitcode, der nicht 0
ist.
# Runs the tests of a project as a CI step: ACCDB and ACCDE, input files in the work folder, failed checks listed. param([string] $Project = 'Orders.acproj', [string] $Results = 'test-results') $worst = 0 foreach ($target in 'accdb', 'accde') { $work = Join-Path $Results $target New-Item -ItemType Directory $work -Force | Out-Null Copy-Item fixtures\* $work -Recurse -Force # the tests read them from CurrentProject.Path $run = wkl-office test $Project -t $target --work $work --json | ConvertFrom-Json $code = $LASTEXITCODE switch ($code) { 0 { "${target}: $($run.passed) checks passed" } 15 { "${target}: $($run.failed) checks failed" $run.checks | Where-Object { -not $_.ok } | ForEach-Object { " FAIL $($_.test): $($_.name) :: $($_.message)" } } 13 { "${target}: time limit: $($run.problem)" } 16 { "${target}: Access ended: $($run.problem)" } default { "${target}: no test run (exit $code): $($run.problem)" } } if ($code -ne 0 -and $worst -eq 0) { $worst = $code } } exit $worst
Danach enthält jeder Arbeitsordner die Kopie der Datenbank, wkl-test-run.jsonl und den JUnit-Bericht
wkl-test-report.xml; der CI-Server archiviert den Ordner test-results. Hielt ein Lauf an (Exitcode 13 oder 16),
stehen die bis dahin geschriebenen Prüfungen in wkl-test-run.jsonl, eine JSON-Zeile je Eintrag; Prüfungen haben
kind gleich check:
Get-Content test-results\accdb\wkl-test-run.jsonl | ForEach-Object { $_ | ConvertFrom-Json } | Where-Object { $_.kind -eq 'check' -and -not $_.ok } | Format-Table test, name, message
Verhalten, das nur außerhalb eines Testlaufs gilt (WklTest.InRun() ist False), prüft das Skript mit call im selben
Arbeitsordner. call ersetzt dort die Kopie der Datenbank durch eine neue Kopie des Build-Ergebnisses; die übrigen
Dateien bleiben, und {work} in einem Argument steht für den Arbeitsordner:
$text = wkl-office call Orders.acproj OrderNumberText 42 --work test-results\accdb if ($LASTEXITCODE -ne 0 -or $text -ne 'A-00042') { throw "OrderNumberText returned '$text' (exit $LASTEXITCODE)" }
Das Modul WklTest
| Element | Aufgerufen von | Zweck |
|---|---|---|
Answer(Kind, Prompt) | der Anwendung | Vor einem Dialog: außerhalb eines Testlaufs Empty (den Dialog zeigen); in einem Lauf die Antwort, die der Test mit Expect festgelegt hat. Ein Dialog, den der Test nicht erwartet oder dessen Text nicht passt, ist eine fehlgeschlagene Prüfung, und die Antwort ist Cancel. |
InRun() | der Anwendung | True, solange wkl-office test läuft. Für Code, der nur fragen darf, wenn jemand antworten kann: Application.UserControl Or WklTest.InRun(). |
Expect Kind, Pattern, Reply | einem Test | Legt den nächsten Dialog fest: seine Art, ein Like-Muster für seinen Text und die Antwort (ein VbMsgBoxResult, ein Text, ein Pfad, ein Array von Pfaden oder WklTest.Cancel). Die Dialoge werden in dieser Reihenfolge erwartet; einer, der nie erschien, ist am Ende der Prozedur eine fehlgeschlagene Prüfung. |
Check(Title, Condition, [Message]) | einem Test | Eine Prüfung: OK oder FAIL mit der Meldung. Gibt die Bedingung zurück. |
Progress Text | einem Test | Eine Fortschrittszeile in der Laufdatei, für lange Tests. |
Cancel() | einem Test | Die Antwort, die für das Schließen eines Dialogs steht: Null (keine Schaltfläche, ein leerer Text, kein Pfad). |
KindMessage, KindInput, KindOpen, KindSaveAs, KindFolder | beiden | Die Dialogarten: msgbox, input, open, saveas, folder. |
Das Modul gehört dem SDK: Jeder Build ersetzt ein Modul WklTest der Quellen durch das eigene, und der nächste Export
schreibt es in die Quellen. Ändern Sie es nur über eine neue Version des SDK. Eine Anwendung, die schon vor dem Build
mit dem SDK kompilieren soll, kann ein Platzhaltermodul WklTest behalten: mit Answer (gibt Empty zurück), InRun
(gibt False zurück), den Konstanten KindMessage bis KindFolder mit den Werten der Tabelle und Cancel (gibt Null
zurück), wenn der Code es verwendet.
Hinweise
- Namen des Moduls.
WklTestbringt öffentliche Namen in das Projekt:InRun,Cancel,Answer,Expect,Check,Progressund die KonstantenKindMessagebisKindFolder. Hat ein Standardmodul der Anwendung ein öffentliches Element gleichen Namens und verwendet der Code es ohne Modulnamen, meldet VBA „Mehrdeutiger Name“, und das Projekt lässt sich nicht mehr kompilieren. Nennen Sie das Modul mit:WklTest.KindOpenfür das SDK,<Modul>.KindOpenfür das eigene Element; die Beispiele dieser Seite schreiben immerWklTest.. - Nichts startet von selbst. Die Kopie wird wie bei
callohne Startformular, Menüband-Rückrufe und AutoExec-Makro geöffnet; eine Datenbank mit AutoExec-Makro wird abgelehnt. Ein Test, der die Vorbereitung des Startformulars braucht, ruft diesen Code selbst auf. - Eine Prozedur nach der anderen. Die Variablen der Module behalten ihre Werte von einer Prozedur zur nächsten, ein Laufzeitfehler setzt sie aber zurück; lassen Sie keinen Test von einem früheren abhängen.
- Dialoge, die
WklTest.Answernicht fragen, werden mit Abbrechen, Nein oder OK beantwortet, und die Prozedur schlägt fehl. Ein mitacDialoggeöffnetes Formular wartet bis zum Zeitlimit; öffnen Sie solche Formulare im Test ohneacDialogund prüfen Sie sie direkt. Der Laufzeitfehlerdialog von VBA erscheint etwa eine Sekunde lang auf dem Desktop, auch wenn Access verborgen läuft. - Zeitlimit und Abstürze. Eine Prozedur, die
--timeoutüberschreitet (Vorgabe 120 Sekunden), beendet ihr Access, und der Lauf hält mit Exitcode 13 an. Wird Access während einer Prozedur beendet, hält der Lauf mit Exitcode 16 an und nennt die zuletzt geschriebene Zeile; die Zeilen davor stehen inwkl-test-run.jsonlim Arbeitsordner. - Tests, die nicht mehr laufen, fallen auf: Ein Lauf aller Prozeduren mit weniger Prüfungen als der letzte grüne Lauf schlägt mit der Zeile
check countfehl. - Tests je Sprache.
--language slführt die Prozeduren mit den Anwendungstexten und den WKL-Komponenten auf Slowenisch aus, unabhängig von der Sprache von Office; so prüft ein Rechner jede Sprache der Anwendung (Texte einer Anwendung). Die Anzahl der Prüfungen wird je Sprache geführt. Das setzt die WKL Forms Object Library 1.65 oder höher voraus. - CI-Server lesen
build\<configuration>\test\wkl-test-report.xml(JUnit-XML). Die Tests laufen im Access des Build-Rechners, in dessen Bitbreite. - Der Arbeitsordner (Vorgabe
build\<configuration>\test, sonst--work) wird angelegt, wenn er fehlt, aber nicht geleert:testersetzt darin nur die Kopie der Datenbank undwkl-test-run.jsonl. Dateien, die ein Skript vorher hineinlegt, finden die Tests unterCurrentProject.Path. - Werte für die Tests.
testübergibt den Testprozeduren keine Parameter; Eingaben legen Sie als Dateien in den Arbeitsordner. Unter Windows 11 sieht das verborgene Access zwar die Umgebungsvariablen des aufrufenden Prozesses, das SDK sichert das aber nicht zu. Schreiben Sie keine Kennwörter in den Arbeitsordner: Seine Dateien bleiben nach dem Lauf liegen. - Access Runtime und Bedienung. Die Tests laufen im vollen Access, auch mit
-t accde; ein Lauf unter Access Runtime (/runtime) wird nicht unterstützt. Bedienung mit echten Tasten und Maus in einem sichtbaren Access (Tabulatorfolge, Fokus, Tastenkürzel, Ziehen und Ablegen) prüfttestnicht. - Tests in der ausgelieferten Anwendung. Die Testprozeduren sind Quellen wie jedes andere Modul und werden in jede Datenbank des Projekts gebaut, auch in die ausgelieferte. Um sie aus der Auslieferung herauszuhalten, legen Sie sie in ein eigenes Testprojekt, das das Hauptprojekt als Vorlage nennt (Projektordner).