Datei search_engine_test.dart - flutter-tutorial-de/dart-programming GitHub Wiki
Die Datei search_engine_test.dart enthält die Unittests der Datei search_engine.dart.
Für jede Funktion aus search_engine.dart existiert mindestens ein Test.
import 'dart:io'; import 'package:dgrep/helper.dart'; import 'package:dgrep/search_engine.dart'; import 'package:path/path.dart'; import 'package:test/test.dart';
-
import 'package:dgrep/search_engine.dart';
Damit kennt der Compiler die Definitionen aus search_engine.dart. - Die anderen Importe betreffen das interne Paket
dart:io
und die externen Paketeargs
,test
undpath
.
Unser Unittest braucht Daten, genauer Dateien, die dann durchsucht werden können.
Diese Dateien werden in der Funktion init()
erzeugt.
String init() { final base = join(Directory.systemTemp.path, 'dgrep'); writeString(join(base, 'text1.txt'), string: '''Eine Zeile ohne Zahl. 3 Chinesen mit dem Kontrabass. Blub '''); writeString(join(base, 'text1.data'), string: '333'); final file2 = join(base, 'dir1', 'text2.txt'); writeString(file2, string: 'Teil2\nder Rest steht:\nIn Zeile 3, TEIL 3'); final file3 = join(base, 'dir1', 'text3.text'); writeString(file3, string: '''nix1 nix2 toll3 nix4 toll5 nix6 nix7 nix8 toll9 nix10 '''); return base; }
-
final base = path.join(Directory.systemTemp.path, 'dgrep');
Damit der Test plattformunabhängig ist, also unter Linux und Windows funktioniert, fragen wir mitDirectory.systemTemp.path
den Namen des temporären Verzeichnisses ab, unter Linux ist das '/tmp', unter Windows 'c:\temp' oder ähnliches. Dieses Verzeichnis wird mit der Funktionjoin()
mit dem Unterverzeichnis namensdgrep
ergänzt, so dass inbase
dann unter Linux/tmp/dgrep
und unter Windowsc:\temp\dgrep
steht. -
writeString(join(base, 'text1.data'), string: '333');
-
join(base, 'text1.data')
setzt den Dateinamen aus Basisverzeichnis und Namentext1.data
zusammen. -
writeString
schreibt den String333
in diese Datei.
-
-
writeString(file3, string: ...
Hier wird ein mehrzeiliger String benutzt, der mit jeweils drei Appostrophen umrahmt ist. Das gestaltet den Programmtext übersichtlicher als die Zeilentrenner mit dem Metazeichen\n
einzutragen. -
return base;
Das Ergebnis der Funktion ist das Basisverzeichnis.
group('search', () { test('recursive search', () { final engine = SearchEngine.execute([ r'\d', '*.y~!X', txtFilePattern, '--recursive', ]); expect(engine.lines.length, 3); expect(engine.lines[0], endsWith('text1.txt-2: 3 Chinesen mit dem Kontrabass.')); expect(engine.lines[1], endsWith('text2.txt-1: Teil2')); expect(engine.lines[2], endsWith('text2.txt-3: In Zeile 3, TEIL 3')); }); ... }
- Jeder Unittest ist ein normales Programm, braucht daher eine Funktion
main
. -
SearchEngine.storeResult = true;
Damit die Suchergebnisse in der KlasseSearchEngine
gespeichert werden, setzen wir das statische AttributstoreResult
auftrue
. - Die Tests können mittels der Funktion
group()
gruppiert werden, das steigert die Übersicht. -
test('recursive search', () { ... });
Der eigentliche Test findet in der Callbackfunktion (dem zweiten Parameter) der Funktiontest()
statt. -
final engine = SearchEngine.execute([r'\d', ...]);
- Der Aufruf der statischen Methode
execute()
bekommt die Simulation der Programmargumente übergeben. - Dies entspricht dem Aufruf in der Eingabeaufforderung:
dgrep \d *.y~!X c:\temp\dgrep\*.txt --recursive
- oder in Linux:
dgrep '\d' '*.y~!X' '/tmp/dgrep/*.txt' --recursive
- Der Aufruf der statischen Methode
- Wir suchen also eine Dezimalziffer (als regulärer Ausdruck:
\d
) in den Dateien, die im aktuellen Verzeichnis liegen und die Endung.y~!X
oder im Basisverzeichnis liegen, mit der Endung.txt
. Es wird auch in Unterverzeichnissen (--recursive
) gesucht. Das erste Dateisuchmuster*.y~!X
ist so gewählt, dass damit keine Dateien gefunden werden, aber normalerweise etliche Unterverzeichnisse durchlaufen werden. Wir testen gleichzeitig, ob die Angabe von zwei Dateisuchmustern funktioniert. - Die Funktion
expect()
hat als ersten Parameter den zu testenden Wert, als zweiten den erwarteten Wert. -
expect(engine.lines.length, 3);
- Da wir oben das Attribut
SearchEngine.storeResult
gesetzt haben, landen die Trefferzeilen im Attributlines
, das eine Stringliste ist. Wir erwarten also drei Trefferzeilen.
- Da wir oben das Attribut
-
expect(engine.lines[0], endsWith('text1.txt-2: 3 Chinesen mit dem Kontrabass.'));
- Die Trefferzeile beginnt mit dem vollen Dateinamen, der unter Linux bzw. Windows verschieden ist. Daher prüfen wir nur den Rest der Zeile mit
endsWith()
.
- Die Trefferzeile beginnt mit dem vollen Dateinamen, der unter Linux bzw. Windows verschieden ist. Daher prüfen wir nur den Rest der Zeile mit
- Analog werden die zwei anderen Zeilen getestet.
- Die weiteren Tests laufen nach dem gleichem Schema wie der erste Test ab.
- Jeder Test prüft die Funktion einer Option bzw. die Kombination von Optionen.
Die zweite Gruppe ist mit 'errors' als Beschreibung gekennzeichnet: Sie prüft die Fehlersituationen:
group('errors', () { test('wrong pattern', () { final engine = SearchEngine.execute([r'*', '.']); expect(engine.lines.length, 0); }); test('unknown option', () { final engine = SearchEngine.execute([r'nixda', '*.nix', '--none-known']); expect(engine, isNull); }); test('to few arguments', () { final engine = SearchEngine.execute([]); expect(engine, isNull); }); }); </code> * <code>engine = SearchEngine.execute([r'*', '.']);</code> Hier ist ein unzulässiger regulärer Ausdruck als Textsuchmuster angegeben: <code>*</code> ist nicht erlaubt, vor dem Stern muss ein Zeichen, eine Zeichenklasse oder eine Klammer stehen. Das Ergebnis der Methode <code>execute</code> muss dann <code<null</code> sein. * <code>engine = SearchEngine.execute([r'nixda', '*.nix', '--none-known']);</code> Wir verwenden eine unbekannte Option <code>--none-known</code>. Wieder muss das Ergebnis <code>null</code> sein, was mit der Konstante <code>isNull</code> festgelegt wird. * <code>engine = SearchEngine.execute([]);</code> Keine Argumente anzugeben ist ebenfalls ein Fehler, der als Ergebnis <code>null</code>liefern muss.