Ich versuche herauszufinden, wie ich python setup.py test
das Äquivalent von ausführen kann python -m unittest discover
. Ich möchte kein Skript run_tests.py verwenden und keine externen Testtools (wie nose
oder py.test
) verwenden. Es ist in Ordnung, wenn die Lösung nur unter Python 2.7 funktioniert.
In setup.py
, ich glaube ich etwas zu den hinzufügen müssen test_suite
und / oder test_loader
Felder in Config, aber ich kann scheinen nicht eine Kombination zu finden , die richtig funktioniert:
config = {
'name': name,
'version': version,
'url': url,
'test_suite': '???',
'test_loader': '???',
}
Ist dies nur unittest
mit Python 2.7 möglich?
Zu Ihrer Information, meine Projektstruktur sieht folgendermaßen aus:
project/
package/
__init__.py
module.py
tests/
__init__.py
test_module.py
run_tests.py <- I want to delete this
setup.py
Update : Dies ist möglich mit, unittest2
aber ich möchte etwas Äquivalentes nur mit findenunittest
Von https://pypi.python.org/pypi/unittest2
unittest2 enthält einen sehr einfachen Setuptools-kompatiblen Testkollektor. Geben Sie test_suite = 'unittest2.collector' in Ihrer setup.py an. Dies startet die Testerkennung mit den Standardparametern aus dem Verzeichnis, das setup.py enthält. Daher ist dies möglicherweise am nützlichsten (siehe unittest2 / collector.py).
Im Moment verwende ich nur ein Skript namens run_tests.py
, aber ich hoffe, dass ich dies beseitigen kann, indem ich zu einer Lösung übergehe, die nur verwendet python setup.py test
.
Hier ist das, was run_tests.py
ich entfernen möchte:
import unittest
if __name__ == '__main__':
# use the default shared TestLoader instance
test_loader = unittest.defaultTestLoader
# use the basic test runner that outputs to sys.stderr
test_runner = unittest.TextTestRunner()
# automatically discover all tests in the current dir of the form test*.py
# NOTE: only works for python 2.7 and later
test_suite = test_loader.discover('.')
# run the test suite
test_runner.run(test_suite)
Antworten:
Wenn Sie py27 + oder py32 + verwenden, ist die Lösung ziemlich einfach:
test_suite="tests",
quelle
test_*.py
. Außerdem habe ich herausgefunden, dass das angegebene Verzeichnis tatsächlich rekursiv durchsucht wird, um eine erweiterte Klasse zu findenunittest.TestCast
. Dies ist äußerst nützlich, wenn Sie eine Verzeichnisstruktur haben, in der Sietests/first_batch/test_*.py
und habentests/second_batch/test_*.py
. Sie können einfach angeben,test_suite="tests",
und es wird alles rekursiv aufgenommen. Beachten Sie, dass jedes verschachtelte Verzeichnis eine__init__.py
Datei enthalten muss.Vom Erstellen und Verteilen von Paketen mit Setuptools (Schwerpunkt Mine):
Daher
setup.py
würden Sie in eine Funktion hinzufügen, die eine TestSuite zurückgibt:import unittest def my_test_suite(): test_loader = unittest.TestLoader() test_suite = test_loader.discover('tests', pattern='test_*.py') return test_suite
Dann würden Sie den Befehl
setup
wie folgt angeben :setup( ... test_suite='setup.my_test_suite', ... )
quelle
__init__.py
den Testordner verschoben und darauf verwiesen wurde.setup()
Funktion innerhalb desif __name__ == '__main__':
Blocks imsetup.py
Skript ausgeführt wird. Wenn das Setup-Skript zum ersten Mal ausgeführt wird, wird der if-Block aufgerufen. Beim zweiten Mal wird das Setup-Skript als Modul importiert, sodass der if-Block nicht aufgerufen wird.test_suite
Parameter überhaupt NICHT enthält , aber "python setup.py test" funktioniert immer noch gut für mich. Das ist etwas anderes, was die Dokumentation sagt : „Wenn Sie nicht ein test_suite in Ihrem Setup () -Aufruf, festgelegt haben und bieten keine --test-Suite Option, tritt ein Fehler auf.“ Irgendeine Idee?Sie benötigen keine Konfiguration, um dies zum Laufen zu bringen. Grundsätzlich gibt es zwei Möglichkeiten:
Der schnelle Weg
Benennen Sie Ihr
test_module.py
in ummodule_test.py
(im Grunde genommen_test
als Suffix zu Tests für ein bestimmtes Modul hinzufügen ), und Python findet es automatisch. Stellen Sie einfach sicher, dass Sie Folgendes hinzufügensetup.py
:from setuptools import setup, find_packages setup( ... test_suite = 'tests', ... )
Der lange Weg
So geht's mit Ihrer aktuellen Verzeichnisstruktur:
Unter
tests/__init__.py
möchten Sie dasunittest
und Ihr Unit-Test-Skript importierentest_module
und anschließend eine Funktion zum Ausführen der Tests erstellen. Intests/__init__.py
, in etwa wie folgt eingeben:import unittest import test_module def my_module_suite(): loader = unittest.TestLoader() suite = loader.loadTestsFromModule(test_module) return suite
Die
TestLoader
Klasse hat außerdem andere FunktionenloadTestsFromModule
. Sie können laufendir(unittest.TestLoader)
, um die anderen zu sehen, aber dieser ist am einfachsten zu verwenden.Da Ihre Verzeichnisstruktur so ist, möchten Sie wahrscheinlich, dass Sie
test_module
Ihrmodule
Skript importieren können . Möglicherweise haben Sie dies bereits getan, aber falls Sie dies nicht getan haben, können Sie den übergeordneten Pfad angeben, damit Sie daspackage
Modul und dasmodule
Skript importieren können . Geben Sie oben Folgendes eintest_module.py
:import os, sys sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) import unittest import package.module ...
Fügen Sie dann schließlich
setup.py
dastests
Modul hinzu und führen Sie den von Ihnen erstellten Befehl ausmy_module_suite
:from setuptools import setup, find_packages setup( ... test_suite = 'tests.my_module_suite', ... )
Dann rennst du einfach
python setup.py test
.Hier ist ein Beispiel, das jemand als Referenz gemacht hat.
quelle
Eine mögliche Lösung besteht darin, den
test
Befehl fürdistutils
undsetuptools
/ einfach zu erweiterndistribute
. Dies scheint ein totaler Kluge und viel komplizierter zu sein, als ich es vorziehen würde, aber es scheint, dass alle Tests in meinem Paket beim Ausführen korrekt erkannt und ausgeführt werdenpython setup.py test
. Ich halte mich zurück, um dies als Antwort auf meine Frage auszuwählen, in der Hoffnung, dass jemand eine elegantere Lösung bietet :)(Inspiriert von https://docs.pytest.org/en/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner )
Beispiel
setup.py
:try: from setuptools import setup except ImportError: from distutils.core import setup def discover_and_run_tests(): import os import sys import unittest # get setup.py directory setup_file = sys.modules['__main__'].__file__ setup_dir = os.path.abspath(os.path.dirname(setup_file)) # use the default shared TestLoader instance test_loader = unittest.defaultTestLoader # use the basic test runner that outputs to sys.stderr test_runner = unittest.TextTestRunner() # automatically discover all tests # NOTE: only works for python 2.7 and later test_suite = test_loader.discover(setup_dir) # run the test suite test_runner.run(test_suite) try: from setuptools.command.test import test class DiscoverTest(test): def finalize_options(self): test.finalize_options(self) self.test_args = [] self.test_suite = True def run_tests(self): discover_and_run_tests() except ImportError: from distutils.core import Command class DiscoverTest(Command): user_options = [] def initialize_options(self): pass def finalize_options(self): pass def run(self): discover_and_run_tests() config = { 'name': 'name', 'version': 'version', 'url': 'http://example.com', 'cmdclass': {'test': DiscoverTest}, } setup(**config)
quelle
Eine weitere weniger als ideale Lösung, die leicht von http://hg.python.org/unittest2/file/2b6411b9a838/unittest2/collector.py inspiriert wurde
Fügen Sie ein Modul hinzu, das einen
TestSuite
der erkannten Tests zurückgibt . Konfigurieren Sie dann das Setup, um dieses Modul aufzurufen.Hier ist
discover_tests.py
:import os import sys import unittest def additional_tests(): setup_file = sys.modules['__main__'].__file__ setup_dir = os.path.abspath(os.path.dirname(setup_file)) return unittest.defaultTestLoader.discover(setup_dir)
Und hier ist
setup.py
:try: from setuptools import setup except ImportError: from distutils.core import setup config = { 'name': 'name', 'version': 'version', 'url': 'http://example.com', 'test_suite': 'discover_tests', } setup(**config)
quelle
Das Standardbibliotheksmodul von Python
unittest
unterstützt die Erkennung (in Python 2.7 und höher sowie in Python 3.2 und höher). Wenn Sie diese Mindestversionen annehmen können, können Sie demdiscover
Befehl einfach das Befehlszeilenargument hinzufügenunittest
.Es ist nur eine kleine Änderung erforderlich, um
setup.py
:import setuptools.command.test from setuptools import (find_packages, setup) class TestCommand(setuptools.command.test.test): """ Setuptools test command explicitly using test discovery. """ def _test_args(self): yield 'discover' for arg in super(TestCommand, self)._test_args(): yield arg setup( ... cmdclass={ 'test': TestCommand, }, )
quelle
Dadurch wird run_tests.py nicht entfernt, es funktioniert jedoch mit setuptools. Hinzufügen:
class Loader(unittest.TestLoader): def loadTestsFromNames(self, names, _=None): return self.discover(names[0])
Dann in setup.py: (Ich nehme an, Sie machen so etwas wie
setup(**config)
)config = { ... 'test_loader': 'run_tests:Loader', 'test_suite': '.', # your start_dir for discover() }
Der einzige Nachteil, den ich sehe, ist, dass die Semantik von
loadTestsFromNames
gebogen wird, aber der Befehl setuptools test ist der einzige Verbraucher und ruft ihn auf eine bestimmte Weise auf .quelle