运行manage.py测试时会发生什么?

本文的翻译特别是为Python Web-Developer课程的学生准备的


您使用命令运行测试manage.py test,但是您知道幕后发生了什么吗?测试运行器如何工作,如何将点E和F放置在屏幕上?

在学习Django的工作原理时,您会发现许多用例,例如更改cookie,设置全局标头和记录请求。同样,一旦了解了测试的工作原理,就可以自定义流程,例如以不同的顺序加载测试,在没有单独文件的情况下配置测试参数或阻止传出的HTTP请求。

在本文中,我们将对测试输出进行至关重要的定制,并将显示测试结果的样式从点和字母更改为表情符号。

但是,在编写代码之前,让我们重构测试过程。

测试输出

让我们看一下测试结果。让我们以一个空测试为基础的项目:

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», , .

, .

:

  1. .

  2. .

  3. .

  4. .

  5. / .

  6. .

, 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_loaderunittest, .

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. 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:

, , .

, pytest 700 . - , , Django. , , pytest - . pytest .

, pytest.

, . , - , Django , .

:




All Articles