Dynamischer Python Checker (JACK3): Unterschied zwischen den Versionen

Aus JACK Wiki
Zur Navigation springen Zur Suche springen
(WIP: Python-Checker)
 
(Finalisierung)
Zeile 1: Zeile 1:
{{Warnung|Warnung=Diese Seite befindet sich noch im Aufbau.}}
Der [[Python (JACK3)|Aufgabentyp Python]] lässt sich automatisiert bewerten, indem Testfälle ausgeführt werden. Diese können z. B. einen erwarteter Wert mit dem tatsächlichen Wert vergleichen. Der Python-Checker von JACK3 kann nicht nur Punktzahlen und Feedback zurückgeben, sondern auch Traces aufzeichnen (welche Zeilen ausgeführt wurden und wie die Variablenbelegung beim Testfall war). Es lassen sich beliebig komplexe Testfälle und auch zufallsbasierte Tests schreiben sowie passgenaues Feedback, programmiert in Python, erzeugen.  


Der [[Python (JACK3)|Aufgabentyp Python]] lässt sich automatisiert bewerten, indem Testfälle ausgeführt und z. B. ein erwarteter Wert mit einem tatsächlichen Wert verglichen wird. Der Python-Checker von JACK3 kann nicht nur Punktzahlen und Feedback zurückgeben, sondern auch Traces (welche Zeilen ausgeführt wurden und wie die Variablenbelegung beim Testfall war). Hierdurch lassen sich beliebig komplexe Testfälle und auch zufallsbasierte Tests schreiben sowie passgenaues Feedback erzeugen.  
{{Hinweis|In diesem Artikel werden [https://docs.python.org/3/tutorial/index.html grundlegende Python-Kenntnisse] vorausgesetzt.}}
 
Der Checker arbeitet auf Python 3.11. Aktuell ist es nicht möglich, eigene Module zum Check hinzuzufügen oder Pakete aus dem Python Package Index (PyPI) (über <code>pip</code>) zu laden. Das Paket [https://pypi.org/project/pandas/ pandas] ist vorinstalliert und kann durch den Testtreiber geladen werden, ansonsten sind ausschließlich Pakete der [https://docs.python.org/3.11/library/index.html Python-Standardbibliothek] nutzbar.
 
=== Unterstützte Aufgabenstellungen ===
Der dynamische Python-Checker ist darauf ausgelegt, Funktionsaufrufe aufzuzeichnen und ihre Rückgabewerte zu prüfen. Dies funktioniert am besten mit folgenden Aufgabenstellungen:
 
* Schreiben Sie eine parameterlose Funktion, die ... zurückgibt.
* Schreiben Sie eine Funktion, die ... erwartet und ... zurückgibt.
* Schreiben Sie eine Funktion mit x Parametern, die mit dem ersten Parameter ... macht. (Um die Funktion zu prüfen, muss der Typ des ersten Parameters mutierbar sein, z.&nbsp;B. eine Liste.)
 
'''Beispiel''': ''Definieren sie eine Funktion add. Die Funktion soll zwei Parameter entgegennehmen und das Ergebnis der Addition zurückgeben.'' Die erwartete Musterlösung für diesen Fall wäre:<syntaxhighlight lang="python3">
def add(a, b):
  return a + b
</syntaxhighlight>Testfälle (s.u.) können diese Funktion dann mit verschiedenen Werten aufrufen und das Ergebnis prüfen.
 
===Aufbau eines Testtreibers===
Grundsätzlich ist ein Testtreiber ein einzelnes Modul mit einer oder mehreren [https://docs.python.org/3.11/tutorial/controlflow.html#defining-functions parameterlosen Funktionen] ('''Testfälle'''), die nacheinander ausgeführt werden. In der Regel importiert der Testtreiber dabei das Modul mit dem eingereichten (zu testenden) Code. Dessen Name [[Python (JACK3)#Feedback|wird im UI als "Modul-Pfad für studentischen Code"]] festgelegt.
 
Damit die Traces der Testfälle korrekt aufgezeichnet werden, wird jeder Testfall mit dem [https://book.pythontips.com/en/latest/decorators.html Decorator] <code>decorator</code> aus dem vorgegebenen <code>Communicator</code>-Modul gekennzeichnet. Der folgende Code zeigt ein Beispiel mit zwei Testfällen, jeweils ohne Inhalt:<syntaxhighlight lang="python3" line="1">
import Communicator
import Trace
import studentcode as s


Der Checker arbeitet auf Python 3. Aktuell ist es nicht möglich, eigene Module zum Check hinzuzufügen oder Pakete aus dem Python Package Index (PyPI) / <code>pip</code> zu laden. Das Paket [https://pypi.org/project/pandas/ pandas] ist vorinstalliert und kann durch den Testtreiber geladen werden, ansonsten sind ausschließlich Pakete der Python-Standardbibliothek eingebunden.  
@Communicator.decorator
def testcase1():
  # TODO so something with s ...
  pass


{{Hinweis|In diesem Artikel werden [https://docs.python.org/3/tutorial/index.html grundlegende Python-Kenntnisse] vorausgesetzt.}}
@Communicator.decorator
def testcase2():
  # TODO so something with s ...
  pass


Grundsätzlich ist ein '''Testtreiber''' ein einzelnes Modul mit einem einer oder mehreren [https://docs.python.org/3/tutorial/controlflow.html#defining-functions parameterlosen Funktionen] ('''Testfälle'''), die nacheinander ausgeführt werden.
if __name__ == '__main__':
    print('Running test case 1...')
    testcase1()
    print('Running test case 2 ...')
    testcase2()
</syntaxhighlight>


=== API zum Zurückmelden von Ergebnissen ===
===API zum Zurückmelden von Ergebnissen===
Testfälle nutzen das Modul <code>Trace</code>, um Ergebnisse zurückzuliefern. Dieses Modul wird während des dynamischen Checks automatisch geladen und sammelt Punktzahlen und Feedback auf. Es bietet folgende Funktionen:<syntaxhighlight lang="python3">
Innerhalb eines Testfalls werden Ergebnisse über das Modul <code>Trace</code> zurückgeliefert. Dieses Modul sammelt alle Punktzahlen und Feedback auf und berichtet diese am Ende der Ausführung. Es bietet folgende Funktion:<syntaxhighlight lang="python3">
def printResult(result: int, feedback: str = None):
def printResult(result: int, feedback: str = None):
   """
   """
   Reports a result at the given line.
   Reports a result.


   :param result: Points which are reported. Must be between 0 and 100 (both inclusive).
   :param result: Points which are reported. Must be between 0 and 100 (both inclusive).
Zeile 19: Zeile 52:
   """
   """
   pass
   pass
</syntaxhighlight>
</syntaxhighlight>{{Wichtig|In jedem Testfall muss <code>printResult</code> genau ein Mal tatsächlich aufgerufen werden!}}
 
=== Schreiben von Testfällen ===
Der Checker nutzt für das Tracing eine spezielle Kontrollflussanalyse, basierend auf Pythons [https://docs.python.org/3.11/reference/simple_stmts.html#the-assert-statement <code>assert</code>-Schlüsselwort] und Exceptions. Mit <code>assert</code> lassen sich Ausdrücke testen, die resultierende Exception lässt sich anschließend fangen und in Feedback umwandeln.
 
Ein möglicher Testfall 1 für das obige Beispiel wäre dementsprechend:<syntaxhighlight lang="python3" line="1" start="5">
@Communicator.decorator
def testcase1():
  try:
    # Berechnen des Ergebnisses mit dem "studentcode":
    result = s.add(1, 0)
    # zu prüfender Ausdruck:
    assert result == 1
 
    # Bei Erfolg: +25 Punkte
    Trace.printResult(25)
  except Exception:
    # Bei Misserfolg: +0 Punkte mit Feedback-Nachricht
    Trace.printResult(0, f"Erwartete Summe: 1, tatsächlich: {result}.")
</syntaxhighlight>Das Tracing reagiert auf alle Arten von Exceptions und zeigt im Falle eines <code>AssertionError</code>s (entweder händisch geworfen oder über <code>assert</code>) eine generische Fehlermeldung an, die mitteilt, dass der Testfall nicht bestanden wurde, sofern kein benutzerdefiniertes Feedback (zweiter Parameter von <code>printResult</code>) vorgegeben wurde.
 
Pro Testtreiber werden die Punkte aller Testfälle aufsummiert. Bei einer gleichen Gewichtung von vier Testfällen wird jeder Testfall also 25 Punkte im Erfolg zurückmelden. Die summierten Punkte müssen zwischen 0 und 100 (jeweils inklusiv) liegen.
[[Kategorie:Benutzerhandbuch]]
[[Kategorie:Benutzerhandbuch]]
__KEIN_INHALTSVERZEICHNIS__
[[Kategorie:Checker]]
__ABSCHNITTE_NICHT_BEARBEITEN__

Version vom 9. Oktober 2024, 15:05 Uhr

Der Aufgabentyp Python lässt sich automatisiert bewerten, indem Testfälle ausgeführt werden. Diese können z. B. einen erwarteter Wert mit dem tatsächlichen Wert vergleichen. Der Python-Checker von JACK3 kann nicht nur Punktzahlen und Feedback zurückgeben, sondern auch Traces aufzeichnen (welche Zeilen ausgeführt wurden und wie die Variablenbelegung beim Testfall war). Es lassen sich beliebig komplexe Testfälle und auch zufallsbasierte Tests schreiben sowie passgenaues Feedback, programmiert in Python, erzeugen.

Hinweis: In diesem Artikel werden grundlegende Python-Kenntnisse vorausgesetzt.

Der Checker arbeitet auf Python 3.11. Aktuell ist es nicht möglich, eigene Module zum Check hinzuzufügen oder Pakete aus dem Python Package Index (PyPI) (über pip) zu laden. Das Paket pandas ist vorinstalliert und kann durch den Testtreiber geladen werden, ansonsten sind ausschließlich Pakete der Python-Standardbibliothek nutzbar.

Unterstützte Aufgabenstellungen

Der dynamische Python-Checker ist darauf ausgelegt, Funktionsaufrufe aufzuzeichnen und ihre Rückgabewerte zu prüfen. Dies funktioniert am besten mit folgenden Aufgabenstellungen:

  • Schreiben Sie eine parameterlose Funktion, die ... zurückgibt.
  • Schreiben Sie eine Funktion, die ... erwartet und ... zurückgibt.
  • Schreiben Sie eine Funktion mit x Parametern, die mit dem ersten Parameter ... macht. (Um die Funktion zu prüfen, muss der Typ des ersten Parameters mutierbar sein, z. B. eine Liste.)

Beispiel: Definieren sie eine Funktion add. Die Funktion soll zwei Parameter entgegennehmen und das Ergebnis der Addition zurückgeben. Die erwartete Musterlösung für diesen Fall wäre:

def add(a, b):
  return a + b

Testfälle (s.u.) können diese Funktion dann mit verschiedenen Werten aufrufen und das Ergebnis prüfen.

Aufbau eines Testtreibers

Grundsätzlich ist ein Testtreiber ein einzelnes Modul mit einer oder mehreren parameterlosen Funktionen (Testfälle), die nacheinander ausgeführt werden. In der Regel importiert der Testtreiber dabei das Modul mit dem eingereichten (zu testenden) Code. Dessen Name wird im UI als "Modul-Pfad für studentischen Code" festgelegt.

Damit die Traces der Testfälle korrekt aufgezeichnet werden, wird jeder Testfall mit dem Decorator decorator aus dem vorgegebenen Communicator-Modul gekennzeichnet. Der folgende Code zeigt ein Beispiel mit zwei Testfällen, jeweils ohne Inhalt:

import Communicator
import Trace
import studentcode as s

@Communicator.decorator
def testcase1():
  # TODO so something with s ...
  pass

@Communicator.decorator
def testcase2():
  # TODO so something with s ...
  pass

if __name__ == '__main__':
    print('Running test case 1...')
    testcase1()
    print('Running test case 2 ...')
    testcase2()

API zum Zurückmelden von Ergebnissen

Innerhalb eines Testfalls werden Ergebnisse über das Modul Trace zurückgeliefert. Dieses Modul sammelt alle Punktzahlen und Feedback auf und berichtet diese am Ende der Ausführung. Es bietet folgende Funktion:

def printResult(result: int, feedback: str = None):
  """
  Reports a result.

  :param result: Points which are reported. Must be between 0 and 100 (both inclusive).
  :param feedback: Optional Feedback presented to the user.
  """
  pass

Wichtig: In jedem Testfall muss printResult genau ein Mal tatsächlich aufgerufen werden!

Schreiben von Testfällen

Der Checker nutzt für das Tracing eine spezielle Kontrollflussanalyse, basierend auf Pythons assert-Schlüsselwort und Exceptions. Mit assert lassen sich Ausdrücke testen, die resultierende Exception lässt sich anschließend fangen und in Feedback umwandeln.

Ein möglicher Testfall 1 für das obige Beispiel wäre dementsprechend:

@Communicator.decorator
def testcase1():
  try:
    # Berechnen des Ergebnisses mit dem "studentcode":
    result = s.add(1, 0)
    # zu prüfender Ausdruck:
    assert result == 1

    # Bei Erfolg: +25 Punkte
    Trace.printResult(25)
  except Exception:
    # Bei Misserfolg: +0 Punkte mit Feedback-Nachricht
    Trace.printResult(0, f"Erwartete Summe: 1, tatsächlich: {result}.")

Das Tracing reagiert auf alle Arten von Exceptions und zeigt im Falle eines AssertionErrors (entweder händisch geworfen oder über assert) eine generische Fehlermeldung an, die mitteilt, dass der Testfall nicht bestanden wurde, sofern kein benutzerdefiniertes Feedback (zweiter Parameter von printResult) vorgegeben wurde.

Pro Testtreiber werden die Punkte aller Testfälle aufsummiert. Bei einer gleichen Gewichtung von vier Testfällen wird jeder Testfall also 25 Punkte im Erfolg zurückmelden. Die summierten Punkte müssen zwischen 0 und 100 (jeweils inklusiv) liegen.