Python unittest übergeben Argumente

74

Wie würde ich in Python ein Argument von der Befehlszeile an eine unittest-Funktion übergeben? Hier ist der Code bis jetzt ... Ich weiß, dass er falsch ist.

class TestingClass(unittest.TestCase):

    def testEmails(self):
        assertEqual(email_from_argument, "[email protected]")


if __name__ == "__main__":
    unittest.main(argv=[sys.argv[1]])
    email_from_argument = sys.argv[1] 
Christopher H.
quelle
6
Die Sache ist, dass Unit-Tests automatisch ausgeführt werden können sollten, was das Übergeben von Argumenten schwierig macht. Vielleicht, wenn Sie mehr darüber erklärt haben, was Sie testen möchten?
Kojiro
1
Wenn Sie in der Befehlszeile ausführen, können Sie python testfunction.py ausführen, und der Name == "main" ermöglicht dies. Ich möchte in der Lage sein, Python testfunction.py [email protected] [email protected]
Christopher H
1
Wie wäre es mit einer Konfigurationsdatei? Würde das dienen? Zum Beispiel Nose TestConfig
Kojiro
1
Ich würde es vorziehen, nicht ... nur eine Präferenz, aber vielleicht sollte ich
Christopher H

Antworten:

139

Also sagen die Ärzte hier: "Sie sagen, das tut weh? Dann tun Sie das nicht!" sind wahrscheinlich richtig. Aber wenn Sie wirklich wollen, gibt es eine Möglichkeit, Argumente an einen Test zu übergeben:

import sys
import unittest

class MyTest(unittest.TestCase):
    USERNAME = "jemima"
    PASSWORD = "password"

    def test_logins_or_something(self):
        print('username : {}'.format(self.USERNAME))
        print('password : {}'.format(self.PASSWORD))


if __name__ == "__main__":
    if len(sys.argv) > 1:
        MyTest.USERNAME = sys.argv.pop()
        MyTest.PASSWORD = sys.argv.pop()
    unittest.main()

das lässt dich laufen mit:

python mytests.py ausername apassword

Sie benötigen das argv.pops, damit Ihre Befehlszeilenparameter nicht mit den eigenen von unittest herumspielen ...

[Update] Das andere, was Sie vielleicht untersuchen möchten, ist die Verwendung von Umgebungsvariablen:

import os
import unittest

class MyTest(unittest.TestCase):
    USERNAME = "jemima"
    PASSWORD = "password"

    def test_logins_or_something(self):
        print('username : {}'.format(self.USERNAME))
        print('password : {}'.format(self.PASSWORD))


if __name__ == "__main__":
    MyTest.USERNAME = os.environ.get('TEST_USERNAME', MyTest.USERNAME)            
    MyTest.PASSWORD = os.environ.get('TEST_PASSWORD', MyTest.PASSWORD)
    unittest.main()

Damit können Sie laufen mit:

TEST_USERNAME=ausername TEST_PASSWORD=apassword python mytests.py

und es hat den Vorteil, dass Sie nicht mit der Analyse der eigenen Argumente von unittest herumspielen. Nachteil ist, dass es unter Windows nicht so funktioniert ...

hwjp
quelle
Sieht aus wie pytest Seite verschoben. Es ist jetzt bei docs.pytest.org
Evgen
1
Zu Ihrer Information, ich habe in der Vergangenheit so etwas getan: Verwenden Sie "-", um meine Argumente von denen zu trennen, die Unittest vielleicht wollen; dann kann ich die Liste der Parameter nach dem "-" an meinen eigenen Arg-Parser übergeben und sie aus sys.argv entfernen oder die Argumente unittest explizit übergeben, indem ich unittest.main (argv = kleineres_Liste) möchte
Joshua Richardson
1
Die sys.argv.pop()Methode scheint nicht zu funktionieren python3. Ich würde gerne wissen, wie das gemacht werden kann python3.
Yeow_Meng
2
@Yeow_Meng, müssen Sie möglicherweise genauer sein. Ich benutze das mit Python-3.6.5 und es funktioniert.
R2evans
Sehr praktisch, wenn Sie dieselben Tests für verschiedene Umgebungen wie verschiedene Phasen usw. ausführen möchten. Wahrscheinlich sind Umgebungsvariablen am saubersten.
Samantha Atkins
27

Eine andere Methode für diejenigen, die dies trotz der richtigen Bemerkungen, die Sie nicht tun sollten, wirklich tun möchten:

import unittest

class MyTest(unittest.TestCase):

    def __init__(self, testName, extraArg):
        super(MyTest, self).__init__(testName)  # calling the super class init varies for different python versions.  This works for 2.7
        self.myExtraArg = extraArg

    def test_something(self):
        print(self.myExtraArg)

# call your test
suite = unittest.TestSuite()
suite.addTest(MyTest('test_something', extraArg))
unittest.TextTestRunner(verbosity=2).run(suite)
steffens21
quelle
Gutes Beispiel! Ein weiterer unter agiletesting.blogspot.com/2005/01/… . Aber Sorgfalt bestanden Argument nicht mit den Unittest
verwirren
Vielen Dank. Dies war hilfreich, um herauszufinden, wie ich für meinen speziellen Fall die geringste Arbeit leisten kann.
Benny Jobigan
1
Kann ich es in großen Mengen tun? Wenn es mehrere Tests in der MyTest-Klasse gibt
Alex
Können Sie mir bitte sagen, warum Sie unitest.main () nicht verwendet und stattdessen TestSuite / TextTestRunner verwendet haben?
Variable
5

Auch wenn die Testgurus sagen, wir sollten es nicht tun: Ich tue es. In manchen Zusammenhängen ist es sehr sinnvoll, Parameter zu haben, um den Test in die richtige Richtung zu lenken, zum Beispiel:

  • Welche der Dutzend identischen USB-Karten sollte ich jetzt für diesen Test verwenden?
  • Welchen Server soll ich jetzt für diesen Test verwenden?
  • Welches XXX soll ich verwenden?

Für mich ist die Verwendung der Umgebungsvariablen für diese Puprose gut genug, da Sie keinen dedizierten Code schreiben müssen, um Ihre Parameter weiterzugeben. es wird von Python unterstützt. Es ist sauber und einfach.

Natürlich befürworte ich keine vollständig parametrierbaren Tests. Aber wir müssen pragmatisch sein und wie gesagt, in manchen Zusammenhängen brauchen Sie ein oder zwei Parameter. Wir sollten nicht darüber nachdenken :)

import os
import unittest


class MyTest(unittest.TestCase):
    def setUp(self):
        self.var1 = os.environ["VAR1"]
        self.var2 = os.environ["VAR2"]

    def test_01(self):
        print("var1: {}, var2: {}".format(self.var1, self.var2))

Dann von der Kommandozeile (unter Linux getestet)

$ export VAR1=1
$ export VAR2=2
$ python -m unittest MyTest
var1: 1, var2: 2
.
----------------------------------------------------------------------
Ran 1 test in 0.000s

OK
Federico
quelle
3

Wenn Sie den Ansatz von steffens21 mit verwenden möchtenunittest.TestLoader , können Sie den ursprünglichen Testlader ändern (siehe unittest.py):

import unittest
from unittest import suite

class TestLoaderWithKwargs(unittest.TestLoader):
    """A test loader which allows to parse keyword arguments to the
       test case class."""
    def loadTestsFromTestCase(self, testCaseClass, **kwargs):
        """Return a suite of all tests cases contained in 
           testCaseClass."""
        if issubclass(testCaseClass, suite.TestSuite):
            raise TypeError("Test cases should not be derived from "\
                            "TestSuite. Maybe you meant to derive from"\ 
                            " TestCase?")
        testCaseNames = self.getTestCaseNames(testCaseClass)
        if not testCaseNames and hasattr(testCaseClass, 'runTest'):
            testCaseNames = ['runTest']

        # Modification here: parse keyword arguments to testCaseClass.
        test_cases = []
        for test_case_name in testCaseNames:
            test_cases.append(testCaseClass(test_case_name, **kwargs))
        loaded_suite = self.suiteClass(test_cases)

        return loaded_suite 

# call your test
loader = TestLoaderWithKwargs()
suite = loader.loadTestsFromTestCase(MyTest, extraArg=extraArg)
unittest.TextTestRunner(verbosity=2).run(suite)
sfinkens
quelle
1
Woher kommt die Methode self.getTestCaseNames?
Alex
Es gehört zur unittest.TestLoader-Klasse, von der TestLoaderWithKwargs erbt.
Sfinkens
1

Habe das gleiche Problem. Meine Lösung besteht darin, nach dem Parsen von Argumenten mit argparse oder auf andere Weise Argumente aus sys.argv zu entfernen

sys.argv = sys.argv[:1]  

Bei Bedarf können Sie unittest Argumente aus main.parseArgs () filtern.

Denis Solovev
quelle
-3

Unit-Tests dienen zum Testen der grundlegenden Funktionen (Funktionen der untersten Ebene der Anwendung), um sicherzustellen, dass Ihre Anwendungsbausteine ordnungsgemäß funktionieren. Es gibt wahrscheinlich keine formale Definition dessen, was das genau bedeutet, aber Sie sollten andere Arten von Tests für die größere Funktionalität in Betracht ziehen - siehe Integrationstests . Das Unit-Test-Framework ist möglicherweise nicht ideal für diesen Zweck.

pepr
quelle
5
Ja, aber das unittest Framework selbst kann verwendet werden (und wird in vielen Fällen verwendet), um Test-Frameworks zu entwickeln und zu integrieren, was häufig ein komplexes Setup und Teardown erfordert. In einem solchen Fall wäre es hilfreich, eine Basisklasse mit Testfunktionen und Parametern und wenn möglich mit einer geerbten Klasse mit der Parameterdefinition zu haben oder ein umgebungsbasiertes Konfigurationsskript zu verwenden.
Umar