Christian H. Kuhn
2017-09-01 12:31:15 UTC
Hallo Gemeinde,
Ein Programm verwendet Input von Dateien im Filesystem, deren Name
hardcoded ist. Die Originalfiles sind zu groß, um sie in Tests zu
benutzen. Ich möchte Mocks für diese Dateien verwenden.
_Zweck des Programms:_ Deutscher Schachbund (DSB) und Weltschachverband
(FIDE) veröffentlichen Listen der Wertungszahlen ihrer Spieler. Der DSB
vergibt nur eine Wertung (DWZ), die FIDE deren drei (ELO für Standard,
Rapid und Blitz). Das Turniersystem „Schweizer System“ benutzt
Wertungszahlen zum Erstellen einer Startrangliste. Das in Deutschland
meistverwendete Programm „SwissChess“ ermöglicht die schnelle Übernahme
von Wertungszahlen durch sog. Hintergrunddateien. Die bislang offiziell
veröffentlichten Hintergrunddateien tragen dort nur DWZ und Standard-ELO
ein. Für Rapid- und Blitzturniere wäre eine Hintergrunddatei
wünschenswert, in der die Standard-ELO mit der Rapid- bzw. Blitz-ELO
überschrieben ist, sofern existent. Solche Hintergrunddateien erzeugt
mein Programm. Es funktioniert soweit. Bevor ich den Quellcode
veröffentliche, möchte ich automatische Tests erstellen.
_Struktur:_ Ein bash-Script lädt die Wertungsdateien von DSB und FIDE
herunter, entpackt sie, ruft das JAVA-Programm auf, packt dessen Output
und verschiebt ihn in ein Verzeichnis, das über den Webserver erreichbar
ist, und räumt auf.
Das Java-Programm arbeitet zweistufig: Im ersten Schritt werden die
Wertungsdateien geparst und die Daten in eine Postgresql-Datenbank
geschrieben. Im zweiten Schritt werden die gespeicherten Daten aus der
Datenbank ausgelesen und in der richtigen Formatierung in die
Ausgabedateien geschrieben. Für beide Schritte existiert je eine Klasse.
Die Schnittstelle enthält nur je eine statische Funktion
importData(Connection) bzw. exportData(Connection) mit scope package.
Öffentliche Schnittstelle des package ist nur main() von QFdsbStarter.
Dort wird die JDBC-Connection aus Daten aus einem Ini4j-File erzeugt und
dann importData() und exportData() aufgerufen.
_Problem:_ Die Originaldateien enthalten ca. 90k (DSB) bzw. 330k (FIDE)
Spieler. Ein vollständiger Durchlauf dauert ca. 100 min. Zu Testzwecken
wäre es wünschenswert, wenn statt der Originaldateien Testdateien mit
einigen wenigen testrelevanten Datensätzen verwendet werden könnten.
Natürlich ist es möglich, entsprechend vorbereitete Dateien anstelle der
Originaldaten in den entsprechenden Ordner zu kopieren. Ich meine aber
einmal gelernt zu haben, dass es nicht so wahnsinnig toll ist, wenn
automatisierte Tests solche externen Abhängigkeiten haben. Besser wäre
es, wenn stattdessen Mocks benutzt werden könnten. Die Dateien sind aber
hardcoded und die Dateinamen, die erzeugten Paths und Reader innerhalb
der zu testenden Klasse private bzw. lokal. Gibt es dennoch eine
Möglichkeit, aus der Testklasse heraus Mocks zu erzeugen?
Und hier noch die relevanten Auszüge aus dem Quellcode:
package de.qno.fdsb;
import [...]
final class QFdsbImporter {
private static final String ISO_8859_1 = "ISO-8859-1";
private QFdsbImporter() {
// empty
}
static void importData(final Connection _connection) throws
QFdsbException {
clearTables(_connection);
fillFideTables(_connection);
fillDsbTables(_connection);
}
private static void clearTables(final Connection _connection) throws
QFdsbException {
[...] // some DELETE * FROM statements
}
/**
* writes downloaded FIDE rating lists in database
*/
private static void fillFideTables(final Connection _connection)
throws QFdsbException {
[...] // similar do DSB
}
/**
* Writes DSB rating list in database
*/
private static void fillDsbTables(final Connection _connection)
throws QFdsbException {
final LinkedList<String> dsbFiles = new
LinkedList<String>(Arrays.asList("verbaende", "vereine", "spieler"));
Path file;
for (final String dsbFile : dsbFiles) {
file = QFdsbStarter.home().resolve(dsbFile + ".csv");
try (PreparedStatement insertStatement = _connection
.prepareStatement("INSERT INTO verbaende VALUES (?,
?, ?, ?);");
CSVReader reader = new
CSVReaderBuilder(Files.newBufferedReader(file, Charset.forName(ISO_8859_1)))
.withSkipLines(1).build()) { // _hier ist
die entscheidende Stelle_
switch (dsbFile) {
case "verbaende":
writeDsbVerbaendeToTable(reader, insertStatement);
break;
case "vereine":
writeDsbVereineToTable(reader, insertStatement);
break;
case "spieler":
writeDsbSpielerToTable(reader, insertStatement);
break;
default:
break;
}
} catch (SQLException e) {
throw new QFdsbException(
"Problems while writing verbaende data into DSB
database or with insert statement for DSB "
+ dsbFile + " database",
e);
} catch (IOException e) {
throw new QFdsbException("Problems while reading DSB " +
dsbFile + " data file", e);
}
}
}
[...]
}
TIA
QNo
Ein Programm verwendet Input von Dateien im Filesystem, deren Name
hardcoded ist. Die Originalfiles sind zu groß, um sie in Tests zu
benutzen. Ich möchte Mocks für diese Dateien verwenden.
_Zweck des Programms:_ Deutscher Schachbund (DSB) und Weltschachverband
(FIDE) veröffentlichen Listen der Wertungszahlen ihrer Spieler. Der DSB
vergibt nur eine Wertung (DWZ), die FIDE deren drei (ELO für Standard,
Rapid und Blitz). Das Turniersystem „Schweizer System“ benutzt
Wertungszahlen zum Erstellen einer Startrangliste. Das in Deutschland
meistverwendete Programm „SwissChess“ ermöglicht die schnelle Übernahme
von Wertungszahlen durch sog. Hintergrunddateien. Die bislang offiziell
veröffentlichten Hintergrunddateien tragen dort nur DWZ und Standard-ELO
ein. Für Rapid- und Blitzturniere wäre eine Hintergrunddatei
wünschenswert, in der die Standard-ELO mit der Rapid- bzw. Blitz-ELO
überschrieben ist, sofern existent. Solche Hintergrunddateien erzeugt
mein Programm. Es funktioniert soweit. Bevor ich den Quellcode
veröffentliche, möchte ich automatische Tests erstellen.
_Struktur:_ Ein bash-Script lädt die Wertungsdateien von DSB und FIDE
herunter, entpackt sie, ruft das JAVA-Programm auf, packt dessen Output
und verschiebt ihn in ein Verzeichnis, das über den Webserver erreichbar
ist, und räumt auf.
Das Java-Programm arbeitet zweistufig: Im ersten Schritt werden die
Wertungsdateien geparst und die Daten in eine Postgresql-Datenbank
geschrieben. Im zweiten Schritt werden die gespeicherten Daten aus der
Datenbank ausgelesen und in der richtigen Formatierung in die
Ausgabedateien geschrieben. Für beide Schritte existiert je eine Klasse.
Die Schnittstelle enthält nur je eine statische Funktion
importData(Connection) bzw. exportData(Connection) mit scope package.
Öffentliche Schnittstelle des package ist nur main() von QFdsbStarter.
Dort wird die JDBC-Connection aus Daten aus einem Ini4j-File erzeugt und
dann importData() und exportData() aufgerufen.
_Problem:_ Die Originaldateien enthalten ca. 90k (DSB) bzw. 330k (FIDE)
Spieler. Ein vollständiger Durchlauf dauert ca. 100 min. Zu Testzwecken
wäre es wünschenswert, wenn statt der Originaldateien Testdateien mit
einigen wenigen testrelevanten Datensätzen verwendet werden könnten.
Natürlich ist es möglich, entsprechend vorbereitete Dateien anstelle der
Originaldaten in den entsprechenden Ordner zu kopieren. Ich meine aber
einmal gelernt zu haben, dass es nicht so wahnsinnig toll ist, wenn
automatisierte Tests solche externen Abhängigkeiten haben. Besser wäre
es, wenn stattdessen Mocks benutzt werden könnten. Die Dateien sind aber
hardcoded und die Dateinamen, die erzeugten Paths und Reader innerhalb
der zu testenden Klasse private bzw. lokal. Gibt es dennoch eine
Möglichkeit, aus der Testklasse heraus Mocks zu erzeugen?
Und hier noch die relevanten Auszüge aus dem Quellcode:
package de.qno.fdsb;
import [...]
final class QFdsbImporter {
private static final String ISO_8859_1 = "ISO-8859-1";
private QFdsbImporter() {
// empty
}
static void importData(final Connection _connection) throws
QFdsbException {
clearTables(_connection);
fillFideTables(_connection);
fillDsbTables(_connection);
}
private static void clearTables(final Connection _connection) throws
QFdsbException {
[...] // some DELETE * FROM statements
}
/**
* writes downloaded FIDE rating lists in database
*/
private static void fillFideTables(final Connection _connection)
throws QFdsbException {
[...] // similar do DSB
}
/**
* Writes DSB rating list in database
*/
private static void fillDsbTables(final Connection _connection)
throws QFdsbException {
final LinkedList<String> dsbFiles = new
LinkedList<String>(Arrays.asList("verbaende", "vereine", "spieler"));
Path file;
for (final String dsbFile : dsbFiles) {
file = QFdsbStarter.home().resolve(dsbFile + ".csv");
try (PreparedStatement insertStatement = _connection
.prepareStatement("INSERT INTO verbaende VALUES (?,
?, ?, ?);");
CSVReader reader = new
CSVReaderBuilder(Files.newBufferedReader(file, Charset.forName(ISO_8859_1)))
.withSkipLines(1).build()) { // _hier ist
die entscheidende Stelle_
switch (dsbFile) {
case "verbaende":
writeDsbVerbaendeToTable(reader, insertStatement);
break;
case "vereine":
writeDsbVereineToTable(reader, insertStatement);
break;
case "spieler":
writeDsbSpielerToTable(reader, insertStatement);
break;
default:
break;
}
} catch (SQLException e) {
throw new QFdsbException(
"Problems while writing verbaende data into DSB
database or with insert statement for DSB "
+ dsbFile + " database",
e);
} catch (IOException e) {
throw new QFdsbException("Problems while reading DSB " +
dsbFile + " data file", e);
}
}
}
[...]
}
TIA
QNo