# Tests erstellen und verstehen
Trotz der unterschiedlichen Arten von Tests (normale, submit oder eigene Tests), folgen alle der gleichen Syntax.
# Test erstellen
Stelle sicher, dass du oben links in dem Dialog unter Type den Black-Box-Test ausgewählt hast.
Unter Points kannst du die Punkte für den Test angeben. Diese werden dann in der Tabelle mit den Abgaben summiert über alle Tests dargestellt.
Im Tab Test content ist der Inhalt des Tests dargestellt.

Im Tab Test content visually ist der Test visuell dargestellt. In einer ähnlichen Form sieht dann auch die Konsolenausgabe aus.

Im Tab Test settings & files können noch Einstellungen für den Test vorgenommen werden. Außerdem können hier Dateien für den Test hinzugefügt werden, welche in das Verzeichnis (der Hauptdatei) kopiert werden.

TIPP
Beachte, dass aktuell die Einstellungen Max. RAM in kb und Max. disk space in kb nicht angewendet werden.
# Allgemeines zu Tests
In Yapex sollen die Tests dir die Arbeit erleichtern, damit du nicht immer den selben Text tippen musst. Außerdem geben sie dir direkt Feedback zu deiner Lösung und wenn alle Test erfolgreich sind, dann bist du mit deiner Lösung auf einem richtigen Weg.
Es gibt verschiedene Typen von Tests.
Black-Box-Testssimulieren die Eingabe eines Nutzers und prüfen die Ausgabe des Programms.Regex-Testsprüften, ob der Inhalt einer oder mehrere Dateien auf einen regulären Ausdruck (Regex) passt.Syntax-Testssollten nicht mehr verwendet werden. Diese kompilieren das Programm lediglich, was aber beiBlack-Box-Testssowieso implizit getan werden muss.Dateivergleich-Testsvergleichen zwei Dateien zeilenweise auf Gleichheit.
Im Folgenden wird auch nur auf Black-Box-Tests eingegangen, die restlichen Tests werden in Yapex direkt erklärt. Dazu musst du nur den Dialog zum Erstellen oder Bearbeiten eines Tests aufrufen und oben auf Open help in new tab klicken.
Es öffnet sich die Anleitung Test Syntax.
TIPP
Ebenfalls im Dialog zum Bearbeiten bzw. Erstellen sind die Test-Server Notes und Test-Server Limits mit zusätzlichen Informationen zu finden.
# Black-Box-Test
In Yapex sollen die Black-Box-Tests die Eingabe von Nutzern simulieren.
Dabei muss angegeben werden, was der Nutzer eingeben würde und was wir erwarten, was das Programm ausgeben sollte.
Der Inhalt der Tests ist Zeilenbasiert, d.h. jede neue Zeile gibt eine Anweisung an.
Bei Black-Box-Tests gibt das erste Zeichen der Zeile außerdem an, ob es sich um eine Eingabe, eine Ausgabe oder eine andere Anweisung handelt.
WARNUNG
Auch wenn es zunächst komisch aussieht, direkt das zweite Zeichen wird schon als Teil der Anweisung gesehen.
D.h. >1 ist unterschiedlich zu > 1, da hier ein Leerzeichen vor der 1 steht.
Startet eine Zeile mit
$, dann folgen die Konsolenparameter/Kommandozeilenparameter für das Programm.Genau wie auf der Konsole müssen Texte mit Leerzeichen in
""eingeschlossen werden, sonst signalisiert jedes Leerzeichen den Beginn eines neuen Arguments.Die Konsolenparameter/Kommandozeilenparameter sind die einzige Ausnahme, bei der
$ 1und$1gleichbedeutend sind. Beachte aber$" 1"ergibt" 1"mit einem führenden Leerzeichen vor der1.Die Konsolenparameter/Kommandozeilenparameter müssen vor allen anderen Anweisungen kommen (außer Kommentare)
>, dann wird erwartet, dass das Programm den Text hinter dem>ausgibt.- Bitte beachte, dass auf exakte Übereinstimmung geprüft wird, d.h. auch jedes Leerzeichen wird beachtet.
>*, dann wird erwartet, dass das Programm irgendetwas ausgibt, auch wenn es nur der leere String ist.r, dann wird erwartet, dass das Programm etwas ausgibt, was auf den regulären Ausdruck (Regex) hinter demrpasst.<, dann wird der Text hinter<als Eingabe an das Programm gesendet.#, dann stellt die Zeile einen Kommentar dare, dann stellt die Zahl hinter demeden erwarteten Rückgabecode (Exit-Code) des Programms dar.- Ein Wert zwischen
1und255ist sinnvoll, um cross-platform zu bleiben.
0ist nicht sinnvoll, da Programme implizit sowieso mit diesem Exit-Code enden.
- Ein Wert zwischen
# Beispiel
Hier ein praktisches Beispiel, der Inhalt des Tests sei
$math 1
>Welche Konstante (2 Kommastellen)?
<Pi
r3\.14[0-9]*
>Weiter?
<Nein
2
3
4
5
6
Hier geben wir in Zeile 1 an, dass wir die Konsolenparameter/Kommandozeilenparameter
mathund1dem Programm übergeben wollen.In Zeile 2 soll das Programm des Nutzers
Welche Konstante (2 Kommastellen)?ausgeben.In Zeile 3 soll
Pidem Programm des Nutzers übergeben werden.In Zeile 4 soll das Programm des Nutzers etwas ausgeben, was auf den Regex
3\.14[0-9]*passt. Das wäre also3.14,3.141,3.1415, aber auch auch3.14999999.
Vorsicht ist bei.geboten, da dies bei einem Regex für ein beliebiges Zeichen steht. Um dennoch ein.darzustellen, muss man diesen mit\.escapen. Das funktioniert auch für andere Zeichen, die für einen Regex reserviert sind.In Zeile 5 soll das Programm des Nutzers
Weiter?ausgeben.In Zeile 6 soll
Neindem Programm des Nutzers übergeben werden.
Zu beachten ist außerdem auch, dass die genaue Reihenfolge des Tests eingehalten wird. Z.B. dar 3.14 nicht vor Pi ausgegeben werden, sonst schlägt der Test fehl.
TIPP
Wer auch immer durcheinander kommt, was < oder > bedeutet, kann sich vorstellen, dass das Programm des Nutzers auf der linken Seite befindet.
< schiebt Text zum Programm hin (Eingabe)
> nimmt Text vom Programm entgegen (Ausgabe)
r auf eine Eingabe ergibt keinen Sinn, es muss sich also um eine Ausgabe des Programms handeln
TIPP
Bitte beachte, dass aus technischen Gründen immer nur ganze Zeilen als Ein-/Ausgabe verarbeitet werden können.
Dazu gibt es noch einen bekannten Fehler bei der Reihenfolge der Ein-/Ausgabe, der näher in Yapex selbst beschrieben wird in der Anleitung zur Test Syntax (Allgemeines zu Tests). Im Interesse der Kontrolleure (und dir selbst), solltest du dich aber genau an die Reihenfolge der Ein-/Ausgabe halten, es können auch Punkte dafür abgezogen werden (da die Schnittstelle nicht eingehalten wurde)!
# Tests/Konsolenausgabe verstehen
Betrachten wir wieder das Beispiel mit dem Back-Box-Test
$math 1
>Welche Konstante (2 Kommastellen)?
<Pi
r3\.14[0-9]*
>Weiter?
<Nein
2
3
4
5
6
Bei allen Tests können nur Fehler bei der Ausgabe auftreten. Die Eingabe für das Programm wird automatisch vorgenommen.
# Konsolenausgabe (alles ok)
In diesem Fall ist alles gut gelaufen.

Das Programm hat zuerst korrekt
Welche Konstante (2 Kommastellen)?ausgegeben.Danach hat das Programm
Pials (automatische) Eingabe erhalten.Daraufhin hat das Programm korrekterweise
3.14ausgegeben und anschließend auchWeiter?.Zum Schluss wurden dem Programm
Neinals (automatische) Eingabe übergeben und das Programm hat sich beendet und nichts weiter ausgegeben (END OF USER PROGRAM).
# Konsolenausgabe (falsche Ausgabe)
In diesem Fall hat das Programm etwas ausgegeben, jedoch wurde eine andere Ausgabe erwartet.
In diesem Fall wurde eine andere Zeichenkette erwartet, als ausgegeben wurde.

Das Programm hat zuerst korrekt
Welche Konstante (2 Kommastellen)?ausgegeben.Danach hat das Programm
Pials (automatische) Eingabe erhalten.Daraufhin hat das Programm korrekterweise
3.14ausgegeben.Weiterwurde dann vom Programm Ausgegeben, jedoch ist der Text rot (und der Balken am Anfang der Ausgabe). Das bedeutet, dass die Ausgabe nicht mit der erwarteten Ausgabe übereinstimmt.
Die erwartete Ausgabe (Weiter?) ist in der nächsten Zeile zu finden (der Balken ist orange am Anfang der erwarteten Ausgabe).
Wenn du mit der Maus über den orangenen Balken gehst, siehst du auch eine kleine Erklärung dazu.Zum Schluss wurden dem Programm
Neinals (automatische) Eingabe übergeben und das Programm hat sich beendet und nichts weiter ausgegeben (END OF USER PROGRAM).
Da mindestens eine Ausgabe falsch war, war der Test nicht erfolgreich.
# Konsolenausgabe (falsche Ausgabe 2 - Regex)
Die Konsolenausgabe sieht änhlich wie in dem Bild aus, wenn eine Ausgabe nicht auf einen Regex gepasst hat.

Wichtig ist hierbei, dass die Ausgabe nur dem Regex genügen muss. Bei diesem Regex 3\.14[1-9]* muss mindestens 3.14 ausgegeben werden, jedoch können nach 3.14 auch beliebig viele andere Zahlen stehen.
Der Test hätte also auch geklappt, wenn man 3.1415 ausgegeben hätte.
# Konsolenausgabe (falsche Ausgabe 3 - Option Replace Spaces)
Was ist hier passiert? Die Ausgaben sehen identisch aus, sind sie jedoch nicht.

Schaltet man die Option Replace Spaces oben in der Konsolenausgabe an, so erhält man folgendes Bild

Hier sieht man gut, dass die tatsächliche Ausgabe des Programms (rot) am Ende ein Leerzeichen hat, jedoch wurde dort keines erwartet.
# Konsolenausgabe (falsche Ausgabe 4 - Option Use diff)
Manchmal ist es nicht sofort ersichtlich, warum die Ausgaben nicht übereinstimmen. Das ist vor allem bei langen Ausgaben der Fall.

Vielleicht hilft es, wenn du die Option Use diff oben in der Konsolenausgabe einschaltest.

Damit werden nur noch die falschen Zeichen rot dargestellt und die erwarteten Zeichen blau.
# Konsolenausgabe (zu viel ausgegeben)
In diesem Fall ist hat das Programm zu viel ausgegeben.

Alle Aus- und Eingaben wurden korrekt verarbeitet, bis auf die letzte Ausgabe von Welche Konstante (2 Kommastellen)?, die zu viel ist.
Die erwartete Ausgabe gibt außerdem an, dass keine Ausgabe (signalisiert durch die leere Zeile) erwartet wurde.
Außerdem steht die Ausgabe nach ENDE OF USER PROGRAM, was auch drauf schließen lässt, dass die Ausgabe zu viel ist.
# Konsolenausgabe (Timeouts)
Manchmal kommt es auch vor, dass du folgendes siehst

Neben State: steht Program timeout. Das bedeutet, dass ein Timeout überschritten wurde und das Programm daher abgebrochen wurde.
Für einen Timeout gibt es verschiedene Gründe. Die beiden häufigsten Gründe dürfen jedoch folgende sein
# Endlosschleife
z.B.
int i = 0;
int n = 100;
while(i < n) {
System.out.println("Index: " + i);
//... hier steht noch mehr code
}
2
3
4
5
6
Hier wird i nie verändert und daher ist die Bedigung immer erfüllt und das Programm kommt nie zum Ende.
Bei diesem Beispiel ist das leicht ersichtlich, allerdings ist das sehr oft nicht der Fall!
# Endloses Warten auf Eingabe
Das Programm wartet auf eine Eingabe, vom Programm wird jedoch eine Ausgabe erwartet.
# Beispiel 1 - Ein- und Ausgabe vertauscht
Betrachten wir den Test
$math 1
>Welche Konstante (2 Kommastellen)?
<Pi
r3\.14[0-9]*
>Weiter?
<Nein
2
3
4
5
6
entspricht diesem Szenario (wie sich dein Programm verhalten soll):

Und das Programm
Scanner scanner = new Scanner (System.in);
String konstante = scanner.next();
System.out.println("Welche Konstante (2 Kommastellen)?");
//...
2
3
4
Der Test besagt, dass das Programm erst Welche Konstante (2 Kommastellen)? ausgeben soll und danach Pi einlesen soll.
Das Programm liest jedoch erst eine Konstante ein und gibt danach Welche Konstante (2 Kommastellen)? aus (genau andersdrum).
Hier hat also jemand Zeile 2 und 3 vertauscht!
Der tatsächliche Ablauf sieht also so aus:
Das Programm startet und wartet auf eine Eingabe in Zeile 2. Der virtuelle Nutzer wartet auf eine Ausgabe vom Programm (Welche Konstante (2 Kommastellen)?).
Beide würden jetzt ewig warten... aber zum Glück gibt es ein Timeout, was die beiden erlöst.
Es kann außerdem sein, dass du die Ausgabe Could not write to the process siehst, die signalisiert, dass der virtuelle User versucht dem Programm eine Eingabe zu geben, das Programm diese jedoch nicht entgegengenommen hat.
Um den Fehler aufzuspüren, kannst du eine paar zusätzliche Ausgaben einbauen und gucken, wie weit diese ausgegeben werden und ab wann dann keine Ausgabe mehr kommen. Der Fehler liegt dann irgendwo hinter der letzten sichtbaren Ausgabe (im Quellcode).
# Beispiel 2 - Zeilenumbruch vergessen
Gegeben ist folgender Test:
<ausg
>Erledigt: 0%
>Erledigt: [ ]
<ende
2
3
4
und das folgende Programm
import java.io.*;
public class Main {
public static void main(String[] args) throws Exception {
BufferedReader in = new BufferedReader(
new InputStreamReader(System.in));
String cmd = in.readLine();
if (cmd.equals("ausg")) {
System.out.println("Erledigt: 0%");
System.out.print("Erledigt: [ ]");
}
String ende = in.readLine();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
Führt man den Test aus, erhält man Folgendes

Wieder ein Timeout... woarn liegt das?
Zunächst fällt auf, dass die Zeile END OF USER PROGRAM fehlt! Diese Zeile sollte immer kommen, egal ob eine Ausgabe getätigt wurde oder nicht.
Man kann auch sehen, dass Erledigt: 0% erledigt ausgegeben wurde, dass Erledigt: [ ] fehlt jedoch. In Zeile 10 sehen wir System.out.print statt System.out.println, d.h. hinter Erledigt: [ ] wird kein Zeilenumbruch ausgegeben. Alle Ausgaben in Yapex erwarten jedoch am Ende einen Zeilenumbruch!
Dadurch wird ewig auf den Zeilenumbruch gewartet (vom virtuellen Nutzer) und das Programm wird auch nicht beendet, da in Zeile 12 in.readLine steht, was ebenfalls auf eine Eingabe wartet (das in.readLine kann auch manchmal in Testdateien stehen, die nicht selbst von dir verfasst wurden).
Es gibt eine Ausnahme davon, wenn die letzte Zeile im Test eine Ausgabe ist und das Programm anschließend beendet wird, muss das Programm dort kein Zeilenumbruch am Ende ausgeben. (Wird das Programm beendet, wird der Stream geschlossen und fungiert "wie" ein Zeilenumbruch beim Lesen der Ausgabe des Programms)
# Beispiel 3 - mehrere Scanner / BufferedReader
Es kann außerdem zu einem Timeout kommen, wenn du mehrere Instanzen von der Scanner oder der BufferedReader Klasse erstellst (new Scanner(...), new BufferedReader(...)). Yapex benutzt immer den ersten erstellten Scanner bzw. BufferedReader. Schreibst/Liest dein Programm dann von einem anderen Scanner/BufferedReader kann der virtuelle Nutzer keine Eingaben mehr erhalten bzw. senden.
Gerade der Fehler Program timeout ist sehr schwer zu beheben, da gerade das gegenseitige Warten oft nicht leicht ersichtlich ist.
Aus diesem Grund ist es wichtig sich genau an die Reihenfolge der Aus- und Eingaben zu halten.
TIPP
Um den Verlauf besser nachzuvollziehen kannst nach bestimmten Zeilen, bei denen du erwartest, dass das Programm bis da hin kommt eine Ausgabe tätigen.
TIPP
Achte darauf, dass deine Ausgaben einen Zeilenumbruch am Ende haben. Vor allem, wenn du die Zeile END OF USER PROGRAM fehlt.
# Konsolenausgabe (Debug Ausgabe [etwas experimentell])
Da die Tests dann aber fehlschlagen, kannst du auch nach stderr (Standarderror Stream, z.B. System.err) schreiben.

Unterhalb der Option Replace Spaces gibt es noch zwei Funktionen bei dem Bug-Symbol und dem Augen-Symbol (links und rechts neben Program).
Mit dem Bug-Symbol kannst du alle Ausgaben von stderr verstecken. Beachte aber, dass das auch normale Fehlermeldungen versteckt z.B. Division durch 0.
Mit dem Augen-Symbol kannst du die erwarteten Ausgaben verstecken.
WARNUNG
Bitte beachte, dass die Reihenfolge der normalen und debug Ausgabe nicht unbedingt richtig ist.
D.h. die Reihenfolge aller normalen Ausgaben ist richtig und die Reihenfolge aller debug Ausgaben ebenfalls, jedoch die Kombination beider nicht.
Beispielsweise kann also eine debug Ausgabe vor einer normalen Ausgabe kommen, im realen Programm steht die normale Ausgabe jedoch vor der debug Ausgabe.
In den vielen Fällen sollt es jedoch stimmen.
TIPP
Kontrolleure sehen solche Ausgaben jedoch nicht gerne, daher wäre es besser, wenn du sie nach dem Testen wieder entfernst.
# Regex-Test
Neben den Black-Box-Tests wirden Regex-Tests oft benutzt, um zu testen, dass bestimmte Funktionen/Methoden in einer Datei vorhanden sind.
Für einfache Fälle reichen Regex-Tests und es muss nicht der Umweg über Reflection (falls es die Sprache untersützt) gegangen werden.
Trotzdem sind Regex-Tests auch manchmal schweirig, da man daran denken muss, dass die Formatierung des Codes von Nutzer zu Nutzer unterschiedlich sein kann und manchmal mehrere Leerzeichen kommen können.
Ein Regex-Test sieht folgendermaßen aus
mode:gewählter-Modus
connector:gewählte-Verknüpfung
Datei1:regulärer-Ausdruck
Datei2: regulärer-Ausdruck
...
Datei3:regulärer-Ausdruck
*: Regex für alle Dateien
#comment
2
3
4
5
6
7
8
Außerdem darf der regulärer-Ausdruck für eine Datei nicht die Zeichen && (logisch und) und/oder || (logisch oder) enhthalten, da diese zum Verbinden mehrerer regulärer-Ausdrücke reserviert sind. Die Varianten && bzw. || sind erlaubt.
Der mode bestimmt, ob der Tests erfolgreich ist, wenn der Regex auf den Inhalt der Datei passt oder nicht.
Der connector bestimmt, wie der Ergebnisse (bei mehreren Dateien bzw. *) verknüpft werden.
Hier die verschiedenen Kombinationen und Auswirkungen der Optionen.
Wenn als Modus mode:blacklist gewählt wurde, darf bei && als connector keiner der angegebenen regulären Ausdrücke zutreffen. Bei || als connector muss mindestens einer der regulären Ausdrücke nicht zutreffen.
Wenn als Modus mode:whitelist gewählt wurde, müssen bei && als connector alle angegebenen regulären Ausdrücke zutreffen. Bei || als connector muss mindestens einer der regulären Ausdrücke vorkommen.
Wird der mode nicht angegeben, so wird mode:blacklist angenommen. Wird der connector nicht angegeben, so wird && angenommen. Da sich dies in Zukunft ändern kann, solltest du diese immer angeben.
Es können auch mehrere reguläre Ausdrücke für eine Datei angegeben werden. Die regulären Ausdrücke müssen dann mit && oder || verbunden werden.
mode:blacklist
connector:&&
Datei1.java:/$[aA]nfang/ && /^[eE]nde/
Datei2.java:/41;/ && /42;/
2
3
4
# Regex-Test Beispiel
In diesem Regex-Test soll geprüft werden, ob in der Datei SomeSort.java die Funktion sort() aufgerufen wird.
Der Text könnte folgendermaßen aussehen
mode:whitelist
connector:&&
SomeSort.java:/\.\s*sort\(\s*\)/
2
3
Beachte, dass nach dem ., sort und den Klammern beliebig viele Leerzeichen (oder Tabs) kommen können. Aus diesem Grund sind die \s* eingefügt.
Dieser Test würde erfolgreich sein, wenn die Datei SomeSort.java z.B. eine der folgenden Zeilen als Inhalt hat
.sort()
. sort()
. sort( )
. sort( )
.sort( )
2
3
4
5
Jeder andere Text, der auf den Regex passt, wäre aber auch denkbar.
# Regex-Test Ausgabe
Die Ausgabe bei Regex-Tests ist sehr einfach gehalten
used mode: blacklist
used connector: &&
SomeSort.java -> (/\.\s*sort\(\s*\)/) -> true
2
3
Die ersten beiden Zeilen geben den mode und den connector an.
Danach folgt für jede Datei das Ergebnis des jeweiligen Regex. Nach dem Dateinamen wird der Regex wiederholt und am Ende steht das Ergebnis für diese Datei.
Das Ergebnis hängt vom gewählten mode ab und stellt nicht dar, ob der Regex auf den Inhalt gepasst hat oder nicht!
In diesem Beispiel ist das Ergebnis für die Datei false
# test
mode:whitelist
connector:&&
SomeSort.java: /\.\s*sort\(\s*\)/
# datei
.sort(array)
# ausgabe
used mode: whitelist
used connector: &&
SomeSort.java -> (/\.\s*sort\(\s*\)/) -> false
# da .sort() nicht gefunden wurde
2
3
4
5
6
7
8
9
10
11
12
13
In diesem Beispiel ist das Ergebnis für die Datei true, obwohl sich weder der Regex noch der Dateiinhalt geändert hat.
# test
mode:blacklist
connector:&&
SomeSort.java: /\.\s*sort\(\s*\)/
# datei
.sort(array)
# ausgabe
used mode: blacklist
used connector: &&
SomeSort.java -> (/\.\s*sort\(\s*\)/) -> true
# da .sort() nicht gefunden wurde
2
3
4
5
6
7
8
9
10
11
12
13
# Dateien (Assets)
Zu jedem Tests können Dateien (Assets) hinzugefügt werden. Dies ist in dem Dialog zum Erstellen/Ändern eines Tests unter dem Tab Test settings & files zu finden.
Die Dateien werden in das Verzeichnis der Hauptdatei kopiert und können z.B. vom Nutzerprogramm gelesen werden.
TIPP
Dateien können dann sinnvoll sein, wenn der Test zu lang werden würde und man den Inhalt des Tests auch in eine Datei auslagern kann.
Ein Beispiel dafür sind die Dateivergleich-Tests, wobei eine Datei (vom Test) mit einer Datei vergleichen wird, die das Programm des Nutzers erstellt hat.
WARNUNG
Dateien können nur zu einem Test hinzugefügt werden, wenn der Test bereits gespeichert wurde. Um die Tests zu speichern, muss die Aufgabe gespeichert werden!
(Das kommt daher, dass die Dateien mit dem Test verknüpft werden müssen und dazu der Test bereits existieren muss)