
Beispielcode in Dokumentationen sollte aktuell und korrekt sein. Ein Tool wird vorgestellt, das schnell und einfach über JUnit-Tests diese Beispiele erstellt.
Einleitung
Bei der Softwareentwicklung entsteht ein Bedarf an verschiedenen Dokumentationen. Frameworks und Libraries benötigen neben der API-Dokumentation auch Beispielcode für typische Anwendungsfälle.
Das Problem: Damit der Beispielcode in der Dokumentation nicht veraltet, ist es sinnvoll, wenn der Code immer aktuell ist. Idealerweise würde dieser Code in Tests laufen und so Korrektheit garantieren.
Das Konzept: Eine Testklasse erzeugt durch ihre Ausführung die Dokumentation – sowohl Code als auch Ergebnisse können verwendet werden.
Vorbereitung
Die erforderliche Dependency:
<dependency>
<groupId>de.flapdoodle.testdoc</groupId>
<artifactId>de.flapdoodle.testdoc</artifactId>
<version>1.3.1</version>
<scope>test</scope>
</dependency>
Im Surefire-Plugin sollte eine Property für das Ausgabeverzeichnis gesetzt werden:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.19.1</version>
<configuration>
<systemPropertyVariables>
<de.flapdoodle.testdoc.destination>
${project.build.directory}
</de.flapdoodle.testdoc.destination>
</systemPropertyVariables>
</configuration>
</plugin>
Eine erste Dokumentation
Eine JUnit-Extension muss im Test registriert werden:
public class Setup01Test {
@RegisterExtension
public static Recording recording =
Recorder.with("setup01.md", TabSize.spaces(2));
@Test
public void firstTest() {
}
}
Eine entsprechende Markdown-Datei in den test-resources:
# Test 01
Beispiel
Ein umfassenderes Beispiel mit verschiedenen Optionen:
public class Setup02Test {
@RegisterExtension
public static Recording recording =
Recorder.with("setup02.md", TabSize.spaces(2))
.sourceCodeOf("Example", Example.class);
@Test
public void includeSourceCode() {
recording.include(OtherExample.class,
Includes.WithoutPackage,
Includes.Trim, Includes.WithoutImports);
}
@Test
public void codeFromMethod() {
// everything after this marker ...
recording.begin();
boolean sampleVar = true;
assertTrue(sampleVar);
recording.end();
// nothing after this marker
}
@Test
public void multipleCodeBlocks() {
recording.begin();
// first block
recording.end();
recording.begin();
// second block
recording.end();
}
}
Das Dokumentationstemplate im Markdown-Format:
# Test 02
Hier kann beliebiger Text stehen.
## Quellcode aus der Testmethode
...
${codeFromMethod}
...
## mehrere Textblöcke
...
${multipleCodeBlocks}
...
## mehrere Textblöcke - nummeriert
...
${multipleCodeBlocks.1}
...
...
${multipleCodeBlocks.2}
...
## Quellcode einbinden:
...
${Example}
...
...
${includeSourceCode.OtherExample}
...
Wenn das Template fehlt
Fehlt das Template, wird automatisch ein Dokument generiert, das auf das Problem hinweist und die verfügbaren Platzhalter auflistet. So kann man schnell erkennen, welche Parts in der Testklasse aufgezeichnet wurden und wie sie im Template referenziert werden können.
Erläuterung
Das System funktioniert nach einem einfachen Prinzip: Die JUnit-Extension startet am Anfang des Tests. Durch
recording.begin() und recording.end() wird ermittelt, in welchem Test und in welcher Zeile der Aufruf stattfand.
Dies ermöglicht das Kopieren der entsprechenden Textzeilen aus dem Quelltext. Weitere Inhalte wie Klassen können geladen
und verwendet werden. Ausgaben aus Testläufen können mit recording.output(label, content) eingefügt werden.
Wichtig: Die erzeugten Dokumentationen sollten immer den gleichen Inhalt erzeugen, damit sie bei Versionierung nicht bei jedem Testlauf Änderungen aufweisen.
Zusammenfassung
Für kleine bis mittelgroße Projekte bietet dies eine praktische Lösung für aktuelle, korrekte Dokumentation mit Beispielcode. Markdown wurde gewählt, da git-basierte Versionsverwaltungssysteme dieses Format unterstützen und anzeigen können.
Das Projekt: de.flapdoodle.testdoc auf GitHub
