Frage

Ich habe ein Verzeichnis, das meine Python-Komponententests enthält.Jedes Unit-Test-Modul hat die Form test_*.py.Ich versuche, eine Datei mit dem Namen zu erstellen all_test.py Das wird, wie Sie es erraten haben, alle Dateien in der oben genannten Testform ausführen und das Ergebnis zurückgeben.Ich habe bisher zwei Methoden ausprobiert;beide sind gescheitert.Ich werde die beiden Methoden zeigen und hoffe, dass jemand da draußen weiß, wie man das tatsächlich richtig macht.

Bei meinem ersten mutigen Versuch dachte ich: „Wenn ich einfach alle meine Testmodule in die Datei importiere und diese dann aufrufe.“ unittest.main() Doodad, es wird funktionieren, oder?“ Nun, es stellte sich heraus, dass ich falsch lag.

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]

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

Das hat nicht funktioniert, das Ergebnis, das ich bekam, war:

$ python all_test.py 

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Für meinen zweiten Versuch denke ich, ok, vielleicht werde ich versuchen, die ganze Testsache auf eine „manuellere“ Art und Weise durchzuführen.Also habe ich versucht, das unten zu tun:

import glob
import unittest

testSuite = unittest.TestSuite()
test_file_strings = glob.glob('test_*.py')
module_strings = [str[0:len(str)-3] for str in test_file_strings]
[__import__(str) for str in module_strings]
suites = [unittest.TestLoader().loadTestsFromName(str) for str in module_strings]
[testSuite.addTest(suite) for suite in suites]
print testSuite 

result = unittest.TestResult()
testSuite.run(result)
print result

#Ok, at this point I have a result
#How do I display it as the normal unit test command line output?
if __name__ == "__main__":
    unittest.main()

Das hat auch nicht funktioniert, aber es scheint so nah dran zu sein!

$ python all_test.py 
<unittest.TestSuite tests=[<unittest.TestSuite tests=[<unittest.TestSuite tests=[<test_main.TestMain testMethod=test_respondes_to_get>]>]>]>
<unittest.TestResult run=1 errors=0 failures=0>

----------------------------------------------------------------------
Ran 0 tests in 0.000s

OK

Ich scheine eine Art Suite zu haben und kann das Ergebnis ausführen.Ich bin ein wenig besorgt über die Tatsache, dass dort steht, dass ich nur habe run=1, scheint so zu sein run=2, aber es ist ein Fortschritt.Aber wie übergebe ich das Ergebnis und zeige es an main an?Oder wie bringe ich es grundsätzlich zum Laufen, sodass ich einfach diese Datei ausführen und dabei alle Komponententests in diesem Verzeichnis ausführen kann?

War es hilfreich?

Lösung

Sie könnten einen Testläufer verwenden, der dies für Sie tun würde. Nase ist zum Beispiel sehr gut. Beim Ausführen werden Tests im aktuellen Baum gefunden und ausgeführt.

Aktualisiert:

Hier ist ein Code aus meinen Tagen vor der Nase. Sie möchten wahrscheinlich nicht die explizite Liste der Modulnamen, aber vielleicht wird der Rest für Sie nützlich sein.

testmodules = [
    'cogapp.test_makefiles',
    'cogapp.test_whiteutils',
    'cogapp.test_cogapp',
    ]

suite = unittest.TestSuite()

for t in testmodules:
    try:
        # If the module defines a suite() function, call it to get the suite.
        mod = __import__(t, globals(), locals(), ['suite'])
        suitefn = getattr(mod, 'suite')
        suite.addTest(suitefn())
    except (ImportError, AttributeError):
        # else, just load all the test cases from the module.
        suite.addTest(unittest.defaultTestLoader.loadTestsFromName(t))

unittest.TextTestRunner().run(suite)

Andere Tipps

Mit Python 2.7 und höher müssen Sie keinen neuen Code schreiben oder dazu keinen Drittanbieter verwenden. Eine rekursive Testausführung über die Befehlszeile ist integriert.

python -m unittest discover <test_directory>
# or
python -m unittest discover -s <directory> -p '*_test.py'

Sie können mehr in der lesen Python 2.7oder Python 3.x unittestes Dokumentation.

Dies ist jetzt direkt von Unittest möglich: Unittest.Testloader.Discover.

import unittest
loader = unittest.TestLoader()
start_dir = 'path/to/your/test/files'
suite = loader.discover(start_dir)

runner = unittest.TextTestRunner()
runner.run(suite)

In Python 3, wenn Sie verwenden unittest.TestCase:

  • Sie müssen einen leeren haben (oder auf andere Weise) __init__.py Datei in Ihrem test Verzeichnis (muss benannt werden test/)
  • Ihre Testdateien im Inneren test/ passen Sie das Muster an test_*.py. Sie können sich in einem Unterverzeichnis unterhalten test/, und diese Subdirs können als alles andere benannt werden.

Dann können Sie alle Tests mit:

python -m unittest

Fertig! Eine Lösung weniger als 100 Linien. Hoffentlich spart ein anderer Python -Anfänger Zeit, indem er dies findet.

Nun, indem Sie den obigen Code ein wenig untersuchen (speziell verwenden TextTestRunner und defaultTestLoader), Ich konnte mich ziemlich nahe kommen. Schließlich habe ich meinen Code behoben, indem ich auch alle Testsuiten an einen einzelnen Suitenkonstruktor übergeben habe, anstatt sie "manuell" hinzuzufügen, was meine anderen Probleme behoben hat. Also hier ist meine Lösung.

import glob
import unittest

test_files = glob.glob('test_*.py')
module_strings = [test_file[0:len(test_file)-3] for test_file in test_files]
suites = [unittest.defaultTestLoader.loadTestsFromName(test_file) for test_file in module_strings]
test_suite = unittest.TestSuite(suites)
test_runner = unittest.TextTestRunner().run(test_suite)

Ja, es ist wahrscheinlich einfacher, nur die Nase zu verwenden, als dies zu tun, aber das ist nicht der Punkt.

Wenn Sie alle Tests aus verschiedenen Testfallklassen ausführen möchten und Sie gerne explizit angeben möchten, können Sie dies so tun:

from unittest import TestLoader, TextTestRunner, TestSuite
from uclid.test.test_symbols import TestSymbols
from uclid.test.test_patterns import TestPatterns

if __name__ == "__main__":

    loader = TestLoader()
    tests = [
        loader.loadTestsFromTestCase(test)
        for test in (TestSymbols, TestPatterns)
    ]
    suite = TestSuite(tests)

    runner = TextTestRunner(verbosity=2)
    runner.run(suite)

wo uclid ist mein Projekt und TestSymbols und TestPatterns sind Unterklassen von TestCase.

Ich habe das benutzt discover Methode und eine Überlastung von load_tests Um dieses Ergebnis zu einem (minimalen) Zahlenzahlen von Code zu erreichen:

def load_tests(loader, tests, pattern):
''' Discover and load all unit tests in all files named ``*_test.py`` in ``./src/``
'''
    suite = TestSuite()
    for all_test_suite in unittest.defaultTestLoader.discover('src', pattern='*_tests.py'):
        for test_suite in all_test_suite:
            suite.addTests(test_suite)
    return suite

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

Ausführung von Fünfen so etwas wie

Ran 27 tests in 0.187s
OK

Ich habe verschiedene Ansätze ausprobiert, aber alle scheinen fehlerhaft zu sein, oder ich muss einen Code machen, das ist ärgerlich. Aber unter Linux gibt es einen überzeugenden Weg, das nur jeden Test durch ein bestimmtes Muster zu finden und sie dann einzeln aufzurufen.

find . -name 'Test*py' -exec python '{}' \;

Und vor allem funktioniert es definitiv.

Im Falle von a verpackt Bibliothek oder Anwendung möchten Sie es nicht tun. setuptools Werde es für dich tun.

Um diesen Befehl zu verwenden, müssen die Tests Ihres Projekts in a eingewickelt werden unittest Testsuite entweder durch eine Funktion, eine Testpase -Klasse oder -Methode oder ein Modul oder ein Paket, das enthält TestCase Klassen. Wenn die benannte Suite ein Modul ist und das Modul eine hat additional_tests() Funktion, es wird aufgerufen und das Ergebnis (das muss a sein müssen unittest.TestSuite) wird zu den zu durchgeführten Tests hinzugefügt. Wenn die benannte Suite ein Paket ist, Alle Submodule und Unterpackungen werden rekursiv zur gesamten Testsuite hinzugefügt.

Sagen Sie es einfach, wo sich Ihr Root -Testpaket befindet, wie:

setup(
    # ...
    test_suite = 'somepkg.test'
)

Und Renn python setup.py test.

Die Datei-basierte Erkennung kann in Python 3 problematisch sein, es sei denn, Sie vermeiden relative Importe in Ihrer Testsuite, weil discover Verwendet den Dateiimport. Obwohl es optional unterstützt wird top_level_dir, aber ich hatte einige unendliche Rekursionsfehler. Eine einfache Lösung für einen nicht verpackten Code besteht also darin, Folgendes einzulegen __init__.py Ihres Testpakets (siehe load_tests Protokoll).

import unittest

from . import foo, bar


def load_tests(loader, tests, pattern):
    suite = unittest.TestSuite()
    suite.addTests(loader.loadTestsFromModule(foo))
    suite.addTests(loader.loadTestsFromModule(bar))

    return suite

Ich benutze Pydev/Liclipse und habe nicht wirklich herausgefunden, wie man alle Tests gleichzeitig von der GUI ausführt. (Bearbeiten: Klicken Sie mit der rechten Maustaste auf den Ordner root test und wählen Run as -> Python unit-test

Dies ist meine derzeitige Problemumgehung:

import unittest

def load_tests(loader, tests, pattern):
    return loader.discover('.')

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

Ich habe diesen Code in ein Modul genannt all in meinem Testverzeichnis. Wenn ich dieses Modul als unittestes aus Liclipse ausführe, werden alle Tests durchgeführt. Wenn ich bitte nur bestimmte oder fehlgeschlagene Tests wiederholen möchte, werden nur diese Tests durchgeführt. Es stört auch meinen Befehlszeilen -Testläufer (NoSetests) nicht - es wird ignoriert.

Möglicherweise müssen Sie die Argumente ändern discover Basierend auf Ihrem Projekt -Setup.

Basierend auf der Antwort von Stephen Cagle Ich habe Unterstützung für verschachtelte Testmodule hinzugefügt.

import fnmatch
import os
import unittest

def all_test_modules(root_dir, pattern):
    test_file_names = all_files_in(root_dir, pattern)
    return [path_to_module(str) for str in test_file_names]

def all_files_in(root_dir, pattern):
    matches = []

    for root, dirnames, filenames in os.walk(root_dir):
        for filename in fnmatch.filter(filenames, pattern):
            matches.append(os.path.join(root, filename))

    return matches

def path_to_module(py_file):
    return strip_leading_dots( \
        replace_slash_by_dot(  \
            strip_extension(py_file)))

def strip_extension(py_file):
    return py_file[0:len(py_file) - len('.py')]

def replace_slash_by_dot(str):
    return str.replace('\\', '.').replace('/', '.')

def strip_leading_dots(str):
    while str.startswith('.'):
       str = str[1:len(str)]
    return str

module_names = all_test_modules('.', '*Tests.py')
suites = [unittest.defaultTestLoader.loadTestsFromName(mname) for mname 
    in module_names]

testSuite = unittest.TestSuite(suites)
runner = unittest.TextTestRunner(verbosity=1)
runner.run(testSuite)

Der Code sucht alle Unterverzeichnisse von . zum *Tests.py Dateien, die dann geladen werden. Es erwartet von jedem *Tests.py eine einzelne Klasse enthalten *Tests(unittest.TestCase) was nacheinander geladen und ausgeführt wird.

Dies funktioniert mit willkürlicher Tiefenschachtelung von Verzeichnissen/Modulen, aber jedes dazwischen liegende Verzeichnis muss eine leere enthalten __init__.py mindestens Datei. Dadurch kann der Test die verschachtelten Module laden, indem Schrägstriche (oder Backslashes) durch Punkte ersetzt werden (siehe replace_slash_by_dot).

Da die Testentdeckung ein vollständiges Thema zu sein scheint, gibt es ein spezielles Rahmen für die Testen der Entdeckung:

Mehr Lesen hier: https://wiki.python.org/moin/pythontestingtoolstaxonomy

Dieses BASH-Skript führt das Python-Unittest-Testverzeichnis von ÜBERALL im Dateisystem aus, unabhängig davon, in welchem ​​Arbeitsverzeichnis Sie sich befinden:Sein Arbeitsverzeichnis ist immer dort, wo es ist test Verzeichnis befindet.

ALLE TESTS, unabhängige $PWD

Das Unittest-Python-Modul reagiert auf Ihr aktuelles Verzeichnis, es sei denn, Sie teilen ihm mit, wo es sich befindet (mit discover -s Möglichkeit).

Dies ist nützlich, wenn Sie sich im Haus aufhalten ./src oder ./example Arbeitsverzeichnis und Sie benötigen einen kurzen allgemeinen Unit-Test:

#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

python -m unittest discover -s "$readlink"/test -v

AUSGEWÄHLTE TESTS, unabhängige $PWD

Ich nenne diese Dienstprogrammdatei: runone.py und benutze es so:

runone.py <test-python-filename-minus-dot-py-fileextension>
#!/bin/bash
this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

(cd "$dirname"/test; python -m unittest $1)

Keine Notwendigkeit für eine test/__init__.py Datei, um Ihren Paket-/Speicheraufwand während der Produktion zu belasten.

Hier ist mein Ansatz durch Erstellen eine Verpackung Tests aus der Befehlszeile ausführen:

#!/usr/bin/env python3
import os, sys, unittest, argparse, inspect, logging

if __name__ == '__main__':
    # Parse arguments.
    parser = argparse.ArgumentParser(add_help=False)
    parser.add_argument("-?", "--help",     action="help",                        help="show this help message and exit" )
    parser.add_argument("-v", "--verbose",  action="store_true", dest="verbose",  help="increase output verbosity" )
    parser.add_argument("-d", "--debug",    action="store_true", dest="debug",    help="show debug messages" )
    parser.add_argument("-h", "--host",     action="store",      dest="host",     help="Destination host" )
    parser.add_argument("-b", "--browser",  action="store",      dest="browser",  help="Browser driver.", choices=["Firefox", "Chrome", "IE", "Opera", "PhantomJS"] )
    parser.add_argument("-r", "--reports-dir", action="store",   dest="dir",      help="Directory to save screenshots.", default="reports")
    parser.add_argument('files', nargs='*')
    args = parser.parse_args()

    # Load files from the arguments.
    for filename in args.files:
        exec(open(filename).read())

    # See: http://codereview.stackexchange.com/q/88655/15346
    def make_suite(tc_class):
        testloader = unittest.TestLoader()
        testnames = testloader.getTestCaseNames(tc_class)
        suite = unittest.TestSuite()
        for name in testnames:
            suite.addTest(tc_class(name, cargs=args))
        return suite

    # Add all tests.
    alltests = unittest.TestSuite()
    for name, obj in inspect.getmembers(sys.modules[__name__]):
        if inspect.isclass(obj) and name.startswith("FooTest"):
            alltests.addTest(make_suite(obj))

    # Set-up logger
    verbose = bool(os.environ.get('VERBOSE', args.verbose))
    debug   = bool(os.environ.get('DEBUG', args.debug))
    if verbose or debug:
        logging.basicConfig( stream=sys.stdout )
        root = logging.getLogger()
        root.setLevel(logging.INFO if verbose else logging.DEBUG)
        ch = logging.StreamHandler(sys.stdout)
        ch.setLevel(logging.INFO if verbose else logging.DEBUG)
        ch.setFormatter(logging.Formatter('%(asctime)s %(levelname)s: %(name)s: %(message)s'))
        root.addHandler(ch)
    else:
        logging.basicConfig(stream=sys.stderr)

    # Run tests.
    result = unittest.TextTestRunner(verbosity=2).run(alltests)
    sys.exit(not result.wasSuccessful())

Um der Einfachheit zu willen, entschuldigen Sie bitte mein Nicht-NichtsPep8 Codierungsstandards.

Anschließend können Sie die basierteste Klasse für gemeinsame Komponenten für alle Ihre Tests erstellen, sodass jeder Ihrer Test einfach aussieht:

from BaseTest import BaseTest
class FooTestPagesBasic(BaseTest):
    def test_foo(self):
        driver = self.driver
        driver.get(self.base_url + "/")

Zum Ausführen geben Sie einfach Tests als Teil der Befehlszeilenargumente an, z. B.:

./run_tests.py -h http://example.com/ tests/**/*.py
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top