Die Ăśbersetzung des Artikels wurde speziell fĂĽr Studenten des Python Web-Developer- Kurses erstellt .
Sie führen die Tests mit einem Befehl aus manage.py test, aber wissen Sie, was unter der Haube vor sich geht? Wie funktioniert der Testläufer und wie platziert er die Punkte E und F auf dem Bildschirm?
Wenn Sie lernen, wie Django funktioniert, werden Sie viele Anwendungsfälle entdecken, z. B. das Ändern von Cookies, das Festlegen globaler Header und das Protokollieren von Anforderungen. Sobald Sie die Funktionsweise von Tests verstanden haben, können Sie Prozesse anpassen, um beispielsweise Tests in einer anderen Reihenfolge zu laden, Testparameter ohne separate Datei zu konfigurieren oder ausgehende HTTP-Anforderungen zu blockieren.
In diesem Artikel werden wir die Ausgabe Ihrer Tests grundlegend anpassen und den Stil der Anzeige von Testergebnissen von Punkten und Buchstaben zu Emoji ändern.
, , .
. :
from django.test import TestCase
class ExampleTests(TestCase):
def test_one(self):
pass:
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Destroying test database for alias 'default'... , , , -v 3:
$ python manage.py test -v 3
Creating test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')...
Operations to perform:
Synchronize unmigrated apps: core
Apply all migrations: (none)
Synchronizing apps without migrations:
Creating tables...
Running deferred SQL...
Running migrations:
No migrations to apply.
System check identified no issues (0 silenced).
test_one (example.core.tests.test_example.ExampleTests) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Destroying test database for alias 'default' ('file:memorydb_default?mode=memory&cache=shared')..., ! .
«Creating test database…» - Django . , .
SQLite, Django mode=memory . 10. , PostgreSQL, , in-memory.
«Operations to perform» – migrate . , manage.py migrate . , , .
«System check identified no issues». Django, « », . manage.py check, . , , , .
. , , . , .
. test runner , verbosity Django . «testone», , test runner «ok».
, «---». - , . «OK», , .
, .
:
.
.
.
.
/ .
.
, Django .
Django unittest
, , , Django unittest Python. , , , unittest, Django. :
.
«test»
, , — , Django manage.py test. django.core.management.commands.test.
, – 100 . handle() TestRunner. :
def handle(self, *test_labels, **options):
TestRunner = get_runner(settings, options['testrunner'])
...
test_runner = TestRunner(**options)
...
failures = test_runner.run_tests(test_labels)
... TestRunner? Django, . , , Django – django.test.runner.DiscoverRunner. .
DiscoverRunner
DiscoverRunner – . , , - .
- :
class DiscoverRunner:
"""A Django test runner that uses unittest2 test discovery."""
test_suite = unittest.TestSuite
parallel_test_suite = ParallelTestSuite
test_runner = unittest.TextTestRunner
test_loader = unittest.defaultTestLoader, . , – unittest.
, test_runner, , «test runner» — DiscoverRunner Django TextTestRunner unittest. DiscoverRunner , TextTestRunner, . , Django DiscoverRunner -, , TestCoordinator, .
DiscoverRunner runtests(). , run_tests() :
def run_tests(self, test_labels, extra_tests=None, **kwargs):
self.setup_test_environment()
suite = self.build_suite(test_labels, extra_tests)
databases = self.get_databases(suite)
old_config = self.setup_databases(aliases=databases)
self.run_checks(databases)
result = self.run_suite(suite)
self.teardown_databases(old_config)
self.teardown_test_environment()
return self.suite_result(suite, result). , :
setup_databases(). , ,get_databases(),SimpleTestCases, Django .migrate.run_checks().run_suite(), .teardown_databases().
, :
setup_test_environment()teardown_test_environment(), .suite_result().
, . Django. unittest - build_suite() run_suite().
.
buildsuite()
buildsuite() «suite». , , :
def build_suite(self, test_labels=None, extra_tests=None, **kwargs):
suite = self.test_suite()
test_labels = test_labels or ['.']
for label in test_labels:
tests = self.test_loader.loadTestsFromName(label)
suite.addTests(tests)
if self.parallel > 1:
suite = self.parallel_test_suite(suite, self.parallel, self.failfast)
return suite, , , DiscoverRunner:
test_suite- unittest, .parallel_test_suite- , Django.test_loader–unittest, .
runsuite()
DiscoverRunner, – run_suite(). , , :
def run_suite(self, suite, **kwargs):
kwargs = self.get_test_runner_kwargs()
runner = self.test_runner(**kwargs)
return runner.run(suite) – test runner . unittest, . unittest.TextTestRunner - test runner , , , XML- CI-.
, TextTestRunner.
TextTestRunner
unittest - . :
class TextTestRunner(object):
"""A test runner class that displays results in textual form.
It prints out the names of tests as they are run, errors as they
occur, and a summary of the results at the end of the test run.
"""
resultclass = TextTestResult
def __init__(self, ..., resultclass=None, ...): DiscoverRunner, . TextTestResult . DiscoverRunner, resultclass, TextTestRunner._init_().
- . .
, :
, , , DiscoverRunner. , , .
Django :
DiscoverRunner, TESTRUNNER .
– , DiscoverRunner. DiscoverRunner unittest , , .
Test Runner
, . DiscoverRunner runtests(), super():
# example/test.py
from django.test.runner import DiscoverRunner
class SuperFastTestRunner(DiscoverRunner):
def run_tests(self, *args, **kwargs):
print("All tests passed! A+")
failures = 0
return failures:
TEST_RUNNER = "example.test.SuperFastTestRunner" manage.py test, !
$ python manage.py test
All tests passed! A+, !
, !
, TextTestResult unittest . DiscoverRunner, resultclass TextTestRunner.
Django resultclass, , --debug-sql option, .
DiscoverRunner.run_suite() TextTestRunner DiscoverRunner.get_test_runner_kwargs():
<img alt="
def get_test_runner_kwargs(self):
return {
'failfast': self.failfast,
'resultclass': self.get_resultclass(),
'verbosity': self.verbosity,
'buffer': self.buffer,
} get_resultclass(), , (--debug-sql --pdb):
def get_resultclass(self):
if self.debug_sql:
return DebugSQLTextTestResult
elif self.pdb:
return PDBDebugResult , None, TextTestResult resultclass. None TextTestResult:
class EmojiTestRunner(DiscoverRunner):
def get_resultclass(self):
klass = super().get_resultclass()
if klass is None:
return EmojiTestResult
return klass EmojiTestResult TextTestResult . , :
class EmojiTestResult(unittest.TextTestResult):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# If the "dots" style was going to be used, show emoji instead
self.emojis = self.dots
self.dots = False
def addSuccess(self, test):
super().addSuccess(test)
if self.emojis:
self.stream.write('âś…')
self.stream.flush()
def addError(self, test, err):
super().addError(test, err)
if self.emojis:
self.stream.write('?')
self.stream.flush()
def addFailure(self, test, err):
super().addFailure(test, err)
if self.emojis:
self.stream.write('❌')
self.stream.flush()
def addSkip(self, test, reason):
super().addSkip(test, reason)
if self.emojis:
self.stream.write("âŹ")
self.stream.flush()
def addExpectedFailure(self, test, err):
super().addExpectedFailure(test, err)
if self.emojis:
self.stream.write("❎")
self.stream.flush()
def addUnexpectedSuccess(self, test):
super().addUnexpectedSuccess(test)
if self.emojis:
self.stream.write("✳️")
self.stream.flush()
def printErrors(self):
if self.emojis:
self.stream.writeln()
super().printErrors() TEST_RUNNER EmojiTestRunner, :
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
?❎❌âŹâś…✅✅✳️
...
----------------------------------------------------------------------
Ran 8 tests in 0.003s
FAILED (failures=1, errors=1, skipped=1, expected failures=1, unexpected successes=1)
Destroying test database for alias 'default'...!
, unittest . , .
, , . , , . , , , . - unittest.
, DiscoverRunner:
unittest-xml-reporting XML CI-.
, , .
, pytest 700 . - , , Django. , , pytest - . pytest .
, pytest.
, . , - , Django , .