Frage

Ich bin ein Grader für einen Beginn der Programmierkurs mit Python. Mein Python-Fu ist selbst nicht so stark, aber ich möchte versuchen, etwas von der Einstufung zu automatisieren.

Wenn ich online schaue, mag ich das Pyunit Tests Suite, obwohl es wahrscheinlich ein bisschen überwältigt ist, was ich will.

Mein Problem ist, dass ich nicht sicher bin, wie ich die Testeingaben übergeben soll, die ich an die Funktionen des Schülers möchte, da sie keine Befehlszeilenargumente oder sogar mehrere Funktionen verwenden, sondern die Benutzereingabe durch die Benutzereingaben erhalten, die durch die input() Funktion.

Ein dummes Beispiel:

#/usr/bin/python3.1
# A silly example python program
def main():
    a = int(input("Enter an integer: "))
    b = int(input("Enter another integer: "))
    c = a+b
    print("The sum is %d" % c)

if __name__ == '__main__'
    main()

Wie würde ich für mein dummes Beispiel einen Unit -Test schreiben, der die Ausgabe auf verschiedene Eingänge überprüfen könnte? (dh, wenn ich 2 und 3 an den Eingang übergehe, sollte die Ausgangszeichenfolge "Die Summe ist 5") sein.)

War es hilfreich?

Lösung

Bearbeiten: Dies schlägt dies nur vor, da das Beispiel nicht unendlich möglich ist (und ich gehe davon aus, dass die Anfänger-Schüler nur durch die Einschränkung verwirrt sind).

Wenn Sie sich nur um die Ausgabe kümmern, die das entspricht, wonach Sie suchen, warum nicht einfach eine "alberne" Bash verwenden? Etwas wie:

echo -e "2\n3" | python test.py | grep -q "The sum is 5" && echo "Success"

Wenn Sie relativ triviale Programme wie diesen durchführen, sollte dies eine ausreichende oder gut genug sein, die wenig Aufwand erfordert.

Andere Tipps

Das kann man nicht wirklich testen. Eines der Dinge beim Schreiben von Unit-Tests ist, dass Sie häufig Ihren Code anders schreiben müssen, damit er einheitlich getestet wird. In diesem Fall müssten Sie die Aufrufe zur Eingabe in eine separate Funktion ausgeben, die Sie dann patchen können.

def my_input(prompt):
    return input(prompt)

def main():
    a = int(eval(my_input("Enter an integer: "))

usw. Jetzt kann Ihr Test Affenpatch myscript.my_input um die gewünschten Werte zurückzugeben.

Wenn Sie eine kompliziertere Interaktion mit Befehlszeilenprogrammen benötigen, als dies zur Verfügung gestellt werden kann echo Dann möchten Sie sich vielleicht ansehen expect.

Von dem Dokumente:

Die Objekte sys.stdin, sys.stdout und sys.stderr werden in die Dateiobjekte initialisiert, die den Standardeingangs-, Ausgabe- und Fehlerströmen des Interpreter entsprechen.

Nach dieser Logik scheint so etwas zu funktionieren. Erstellen Sie eine Datei mit Ihrer erforderlichen Eingabe:

$ cat sample_stdin.txt
hello
world

Dann umleiten sys.stdin auf diese Datei verweisen:

#!/usr/bin/env python
import sys

fh = open('sample_stdin.txt', 'r')
sys.stdin = fh

line1 = raw_input('foo: ')
line2 = raw_input('bar: ')

print line1
print line2

Ausgabe:

$python redirecting_stdin.py
foo: bar: hello
world

Kurze Antwort, tu das nicht. Sie müssen für Testbarkeit entwerfen. Dies bedeutet, dass Sie eine einfache Möglichkeit bieten, Schnittstellen für Dinge bereitzustellen, mit denen Sie mit Systemressourcen sprechen können, damit Sie beim Testen alternative Implementierungen dieser Schnittstellen bereitstellen können.

Die in der andere Antwort beschriebene Affen -Patching -Lösung funktioniert, ist jedoch die primitivste Ihrer Optionen. Persönlich würde ich eine Schnittstellenklasse für die Benutzerinteraktion schreiben. Zum Beispiel:

class UserInteraction(object):
    def get_input(self):
        raise NotImplementedError()
    def send_output(self, output):
        raise NotImplementedError()

Dann können Dinge, die mit dem Benutzer sprechen müssen, eine Instanz Ihrer Klasse als Konstruktor- oder Funktionsargument erhalten. Die Standardimplementierung kann das tatsächliche aufrufen input Funktion oder was auch immer, aber es wird eine Version zum Testen verwendet, die Probeneingang oder Puffer der Ausgabe ermöglicht, damit sie überprüft werden kann.

Dies hasse übrigens, warum ich Singleton hasse (was sowieso in Python nicht wirklich effektiv implementiert werden kann). Es zerstört Ihre Fähigkeit zu testen, indem Sie eine Instanz erstellen, die weltweit zugänglich ist und mit einer Stub -Version zum Testen nicht gestoppt werden kann.

Mein Vorschlag ist, Ihren Code mit einem der beiden Frameworks neu zu gestalten, die Python für Einheitentests bereitstellt: Gerätetest (auch bekannt als Pyunit) und Doktor.

Dies ist ein Beispiel verwendet Gerätetest:

import unittest

def adder(a, b):
    "Return the sum of two numbers as int"
    return int(a) + int(b)

class TestAdder(unittest.TestCase):
    "Testing adder() with two int"
    def test_adder_int(self):
        self.assertEqual(adder(2,3), 5)

    "Testing adder() with two float"
    def test_adder_float(self):
        self.assertEqual(adder(2.0, 3.0), 5)

    "Testing adder() with two str - lucky case"
    def test_adder_str_lucky(self):
        self.assertEqual(adder('4', '1'), 5)

    "Testing adder() with two str"
    def test_adder_str(self):
        self.assertRaises(ValueError, adder, 'x', 'y')

if __name__ == '__main__':
    unittest.main()

Und dies ist ein Beispiel verwendet Doktor:

# adder.py

def main(a, b):
    """This program calculate the sum of two numbers. 
    It prints an int (see %d in print())

    >>> main(2, 3)
    The sum is 5

    >>> main(3, 2)
    The sum is 5

    >>> main(2.0, 3)
    The sum is 5

    >>> main(2.0, 3.0)
    The sum is 5

    >>> main('2', '3')
    Traceback (most recent call last):
        ...
    TypeError: %d format: a number is required, not str
    """
    c = a + b
    print("The sum is %d" % c)

def _test():
    import doctest, adder
    return doctest.testmod(adder)

if __name__ == '__main__':
    _test()

Mit Doktor Ich habe ein weiteres Beispiel mit Input () gemacht (ich gehe davon aus, dass Sie Python 3.x verwenden):

# adder_ugly.py

def main():
    """This program calculate the sum of two numbers.
    It prints an int (see %d in print())

    >>> main()
    The sum is 5
    """
    a = int(input("Enter an integer: "))
    b = int(input("Enter another integer: "))
    c = a+b
    print("The sum is %d" % c)


def _test():
    import doctest, adder_ugly
    return doctest.testmod(adder_ugly)

if __name__ == '__main__':
    _test()

Ich würde jedes der oben genannten Beispiele mit dem ausführen -v Möglichkeit:

python adder_ugly.py -v

Zu Ihrer Referenz siehe:

http://docs.python.org/py3k/library/unittest.html?highlight=unittest#unittest

und

http://docs.python.org/py3k/library/doctest.html#module-doctest

Möglicherweise können Sie das verspotten input Funktionieren Sie Eingaben aus Ihrer Testumgebung.

Dies scheint, als könnte es funktionieren. Es ist ungetestet.

class MockInput( object ):
    def __init__( self, *values ):
        self.values= list(values)
        self.history= []
    def __call__( self, *args, **kw ):
        try:
            response= self.values.pop(0)
            self.history.append( (args, kw, response) )
            return response
        except IndexError:
            raise EOFError()

class TestSomething( unittest.TestCase ):
    def test_when_input_invalid( self ):
        input= MockInput( "this", "and", "that" )
        # some test case based on the input function

Ersetzen Sie Sys.stdin durch ein Stringio- (oder cstringio) -Objekt.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top