Django 2.2.4.dev20190701100356 文档

高级测试主题

请求工厂

class RequestFactory[源代码]

这个 RequestFactory 与测试客户端共享相同的API。但是,与浏览器的行为不同,RequestFactory提供了一种生成请求实例的方法,该实例可以用作任何视图的第一个参数。这意味着您可以像测试任何其他函数一样测试视图函数——作为一个黑盒,使用完全已知的输入,测试特定的输出。

的API RequestFactory 是测试客户端API的一个稍微受限制的子集:

  • 它只能访问HTTP方法 get()post()put()delete()head()options()trace() .
  • 这些方法接受所有相同的参数 除了 对于 follow . 因为这只是一个生产请求的工厂,所以由您来处理响应。
  • 它不支持中间件。如果视图正常工作需要,会话和身份验证属性必须由测试本身提供。

例子

以下是使用请求工厂的简单单元测试:

from django.contrib.auth.models import AnonymousUser, User
from django.test import RequestFactory, TestCase

from .views import MyView, my_view

class SimpleTest(TestCase):
    def setUp(self):
        # Every test needs access to the request factory.
        self.factory = RequestFactory()
        self.user = User.objects.create_user(
            username='jacob', email='jacob@…', password='top_secret')

    def test_details(self):
        # Create an instance of a GET request.
        request = self.factory.get('/customer/details')

        # Recall that middleware are not supported. You can simulate a
        # logged-in user by setting request.user manually.
        request.user = self.user

        # Or you can simulate an anonymous user by setting request.user to
        # an AnonymousUser instance.
        request.user = AnonymousUser()

        # Test my_view() as if it were deployed at /customer/details
        response = my_view(request)
        # Use this syntax for class-based views.
        response = MyView.as_view()(request)
        self.assertEqual(response.status_code, 200)

测试和多个主机名

这个 ALLOWED_HOSTS 运行测试时验证设置。这允许测试客户机区分内部和外部URL。

支持多租户或基于请求的主机以其他方式更改业务逻辑并在测试中使用自定义主机名的项目必须将这些主机包括在 ALLOWED_HOSTS .

第一个也是最简单的选择是将主机添加到设置文件中。例如,docs.djangoproject.com的测试套件包括以下内容:

from django.test import TestCase

class SearchFormTestCase(TestCase):
    def test_empty_get(self):
        response = self.client.get('/en/dev/search/', HTTP_HOST='docs.djangoproject.dev:8000')
        self.assertEqual(response.status_code, 200)

设置文件包括项目支持的域列表:

ALLOWED_HOSTS = [
    'www.djangoproject.dev',
    'docs.djangoproject.dev',
    ...
]

另一个选项是将所需主机添加到 ALLOWED_HOSTS 使用 override_settings()modify_settings() . 对于无法打包自己的设置文件的独立应用程序或域列表不是静态的项目(例如,用于多租户的子域),此选项可能更可取。例如,您可以为域编写一个测试 http://otherserver/ 如下:

from django.test import TestCase, override_settings

class MultiDomainTestCase(TestCase):
    @override_settings(ALLOWED_HOSTS=['otherserver'])
    def test_other_domain(self):
        response = self.client.get('http://otherserver/foo/bar/')

停用 ALLOWED_HOSTS 检查 (ALLOWED_HOSTS = ['*'] )当运行测试时,如果您遵循重定向到外部URL,测试客户端将不会引发有用的错误消息。

测试和多个数据库

测试主/副本配置

如果使用主/副本(某些数据库称为master/slave)复制测试多数据库配置,则创建测试数据库的这种策略会带来问题。创建测试数据库时,不会有任何复制,因此,在主数据库上创建的数据在副本上看不到。

为了弥补这一点,Django允许您定义数据库是 测试镜 . 考虑以下(简化)示例数据库配置:

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myproject',
        'HOST': 'dbprimary',
         # ... plus some other settings
    },
    'replica': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'myproject',
        'HOST': 'dbreplica',
        'TEST': {
            'MIRROR': 'default',
        },
        # ... plus some other settings
    }
}

在这个设置中,我们有两个数据库服务器: dbprimary ,由数据库别名描述 defaultdbreplica 由别名描述 replica . 如你所料, dbreplica 已由数据库管理员配置为的读取副本 dbprimary ,所以在正常活动中,任何写入 default 将出现在 replica .

如果Django创建了两个独立的测试数据库,这将破坏任何预期会发生复制的测试。然而, replica 数据库已配置为测试镜像(使用 MIRROR 测试设置),表示正在测试, replica 应该像镜子一样 default .

配置测试环境时,测试版本为 replicanot 被创造。而是连接到 replica 将被重定向到点 default . 因此,写入 default 将出现在 replica --但因为它们实际上是同一个数据库,而不是因为两个数据库之间存在数据复制。

控制测试数据库的创建顺序

默认情况下,Django将假定所有数据库都依赖于 default 数据库,因此始终创建 default 首先是数据库。但是,不会保证测试设置中任何其他数据库的创建顺序。

如果数据库配置需要特定的创建顺序,则可以使用 DEPENDENCIES 测试设置。考虑以下(简化)示例数据库配置:

DATABASES = {
    'default': {
        # ... db settings
        'TEST': {
            'DEPENDENCIES': ['diamonds'],
        },
    },
    'diamonds': {
        # ... db settings
        'TEST': {
            'DEPENDENCIES': [],
        },
    },
    'clubs': {
        # ... db settings
        'TEST': {
            'DEPENDENCIES': ['diamonds'],
        },
    },
    'spades': {
        # ... db settings
        'TEST': {
            'DEPENDENCIES': ['diamonds', 'hearts'],
        },
    },
    'hearts': {
        # ... db settings
        'TEST': {
            'DEPENDENCIES': ['diamonds', 'clubs'],
        },
    }
}

在这种配置下, diamonds 将首先创建数据库,因为它是唯一没有依赖关系的数据库别名。这个 defaultclubs 下一步将创建别名(尽管不能保证此对的创建顺序),然后 hearts 最后 spades .

如果在 DEPENDENCIES 定义,一个 ImproperlyConfigured 将引发异常。

的高级功能 TransactionTestCase

TransactionTestCase.available_apps

警告

此属性是一个私有API。它可以在未来更改或删除,而不需要使用折旧期,例如为了适应应用程序加载中的更改。

它用于优化Django自己的测试套件,其中包含数百个模型,但不同应用程序中的模型之间没有关系。

默认情况下, available_apps 设置为 None . 每次测试后,Django都会调用 flush 重置数据库状态。这会清空所有的表格并发出 post_migrate 信号,它为每个模型重新创建一个内容类型和四个权限。这个操作会根据模型的数量成比例地增加成本。

设置 available_apps 在应用程序列表中,指示Django的行为就像只有这些应用程序中的模型可用一样。行为 TransactionTestCase 变更如下:

  • post_migrate 在每个测试之前激发,以创建可用应用程序中每个模型的内容类型和权限,以防丢失。
  • 每次测试后,Django只清空与可用应用程序中的模型对应的表。但是,在数据库级别,截断可能会级联到不可用应用程序中的相关模型。此外 post_migrate 不会被解雇,下一个会被解雇 TransactionTestCase ,然后选择正确的应用程序集。

因为数据库没有完全刷新,如果测试创建的模型实例不包括在 available_apps ,它们会泄漏,并可能导致不相关的测试失败。注意使用会话的测试;默认会话引擎将它们存储在数据库中。

自从 post_migrate 不是在刷新数据库后发出的,它的状态是在 TransactionTestCase 和A后面的不一样吗? TestCase :缺少侦听器创建的行 post_migrate . 考虑到 order in which tests are executed ,这也不是问题,只要 TransactionTestCase 在给定的测试套件中声明 available_apps 或者没有。

available_apps 在Django自己的测试套件中是必需的。

TransactionTestCase.reset_sequences

设置 reset_sequences = True 在一 TransactionTestCase 将确保在测试运行之前始终重置序列::

class TestsThatDependsOnPrimaryKeySequences(TransactionTestCase):
    reset_sequences = True

    def test_animal_pk(self):
        lion = Animal.objects.create(name="lion", sound="roar")
        # lion.pk is guaranteed to always be 1
        self.assertEqual(lion.pk, 1)

除非显式测试主键序列号,否则建议不要在测试中硬编码主键值。

使用 reset_sequences = True 会减慢测试速度,因为主键重置是一个相对昂贵的数据库操作。

使用django测试运行程序测试可重用的应用程序

如果你在写 reusable application 您可能希望使用Django测试运行程序来运行自己的测试套件,从而从Django测试基础结构中获益。

通常的做法是 测验 应用程序代码旁边的目录,结构如下:

runtests.py
polls/
    __init__.py
    models.py
    ...
tests/
    __init__.py
    models.py
    test_settings.py
    tests.py

让我们看看这些文件中的几个:

runtests.py
#!/usr/bin/env python
import os
import sys

import django
from django.conf import settings
from django.test.utils import get_runner

if __name__ == "__main__":
    os.environ['DJANGO_SETTINGS_MODULE'] = 'tests.test_settings'
    django.setup()
    TestRunner = get_runner(settings)
    test_runner = TestRunner()
    failures = test_runner.run_tests(["tests"])
    sys.exit(bool(failures))

这是您调用以运行测试套件的脚本。它设置了Django环境,创建了测试数据库并运行了测试。

为了清晰起见,本示例仅包含使用Django测试运行程序所需的最低限度。您可能需要添加命令行选项来控制冗长性、传递要运行的特定测试标签等。

tests/test_settings.py
SECRET_KEY = 'fake-key'
INSTALLED_APPS = [
    "tests",
]

此文件包含 Django settings 需要运行应用程序的测试。

同样,这是一个最小的示例;您的测试可能需要其他设置才能运行。

自从 测验 包包含在 INSTALLED_APPS 运行测试时,可以在其 models.py 文件。

使用不同的测试框架

显然, unittest 不是唯一的python测试框架。虽然Django没有为可选框架提供明确的支持,但它提供了一种调用为可选框架构建的测试的方法,就像它们是普通的Django测试一样。

当你奔运行 ./manage.py test Django看着 TEST_RUNNER 设置以确定要执行的操作。默认情况下, TEST_RUNNER 指向 'django.test.runner.DiscoverRunner' . 此类定义默认的Django测试行为。这种行为包括:

  1. 正在执行全局预测试设置。
  2. 在当前目录下的任何文件中查找名称与模式匹配的测试 test*.py .
  3. 正在创建测试数据库。
  4. 运行 migrate 将模型和初始数据安装到测试数据库中。
  5. 运行 system checks .
  6. 运行找到的测试。
  7. 正在销毁测试数据库。
  8. 正在执行全局测试后拆卸。

如果您定义自己的测试运行程序类和点 TEST_RUNNER 在那节课上,Django将在每次运行时执行测试运行程序 ./manage.py test . 这样,就可以使用任何可以从python代码执行的测试框架,或者修改django测试执行过程以满足您可能拥有的任何测试需求。

定义测试运行程序

测试运行程序是定义 run_tests() 方法。Django与 DiscoverRunner 定义默认Django测试行为的类。此类定义了 run_tests() 入口点,加上用于 run_tests() 设置、执行和删除测试套件。

class DiscoverRunner(pattern='test*.py', top_level=None, verbosity=1, interactive=True, failfast=False, keepdb=False, reverse=False, debug_mode=False, debug_sql=False, **kwargs)[源代码]

DiscoverRunner 将在任何匹配的文件中搜索测试 pattern .

top_level 可用于指定包含顶级python模块的目录。通常,Django可以自动解决这个问题,所以不需要指定这个选项。如果指定,则通常应该是包含 manage.py 文件。

verbosity 确定将打印到控制台的通知和调试信息的数量; 0 没有输出, 1 是正常输出,并且 2 是详细输出。

如果 interactiveTrue ,测试套件有权在执行测试套件时向用户请求指示。这种行为的一个例子是请求删除现有测试数据库的权限。如果 interactiveFalse ,测试套件必须能够在没有任何手动干预的情况下运行。

如果 failfastTrue ,检测到第一个测试失败后,测试套件将停止运行。

如果 keepdbTrue 测试套件将使用现有数据库,或者在必要时创建一个数据库。如果 False 将创建一个新数据库,提示用户删除现有数据库(如果存在)。

如果 reverseTrue ,测试用例将按相反的顺序执行。这对于调试没有正确隔离并且有副作用的测试很有用。 Grouping by test class 使用此选项时保留。

debug_mode 指定 DEBUG 在运行测试之前,应将设置设置为。

如果 debug_sqlTrue ,失败的测试用例将输出记录到 django.db.backends logger 以及追溯。如果 verbosity2 ,然后输出所有测试中的查询。

Django可能会不时地通过添加新参数来扩展测试运行程序的功能。这个 **kwargs 声明允许此扩展。如果你是子类 DiscoverRunner 或者编写自己的测试运行程序,确保它接受 **kwargs .

测试运行程序还可以定义其他命令行选项。创建或重写 add_arguments(cls, parser) 类方法并通过调用 parser.add_argument() 在方法内部,这样 test 命令将能够使用这些参数。

属性

DiscoverRunner.test_suite

用于构建测试套件的类。默认设置为 unittest.TestSuite . 如果您希望实现不同的逻辑来收集测试,则可以覆盖此项。

DiscoverRunner.test_runner

这是低级测试运行程序的类,用于执行单个测试并格式化结果。默认设置为 unittest.TextTestRunner . 尽管命名约定中存在不幸的相似性,但这与 DiscoverRunner 包括一系列更广泛的责任。可以重写此属性以修改运行和报告测试的方式。

DiscoverRunner.test_loader

这是一个类,它从测试用例或模块或其他地方加载测试,并将它们打包到测试套件中,供运行程序执行。默认设置为 unittest.defaultTestLoader . 如果您的测试将以不寻常的方式加载,则可以重写此属性。

方法

DiscoverRunner.run_tests(test_labels, extra_tests=None, **kwargs)[源代码]

运行测试套件。

test_labels 允许您指定要运行的测试并支持多种格式(请参见 DiscoverRunner.build_suite() 获取支持格式的列表)。

extra_tests 是一个额外的列表 TestCase 要添加到由测试运行程序执行的套件中的实例。除了在中列出的模块中发现的测试外,还将运行这些额外的测试。 test_labels .

此方法应返回失败的测试数。

classmethod DiscoverRunner.add_arguments(parser)[源代码]

重写此类方法以添加 test 管理命令。见 argparse.ArgumentParser.add_argument() 有关向分析器添加参数的详细信息。

DiscoverRunner.setup_test_environment(**kwargs)[源代码]

通过调用设置测试环境 setup_test_environment() 设置 DEBUGself.debug_mode (默认为 False

DiscoverRunner.build_suite(test_labels, extra_tests=None, **kwargs)[源代码]

构造与提供的测试标签匹配的测试套件。

test_labels 是描述要运行的测试的字符串列表。测试标签可以采用四种形式之一:

  • path.to.test_module.TestCase.test_method --在测试用例中运行单个测试方法。
  • path.to.test_module.TestCase --在测试用例中运行所有测试方法。
  • path.to.module --在命名的python包或模块中搜索并运行所有测试。
  • path/to/directory --搜索并运行命名目录下的所有测试。

如果 test_labels 具有一定的价值 None ,测试运行程序将在当前目录下的所有文件中搜索与其名称匹配的测试。 pattern (见上文)。

extra_tests 是一个额外的列表 TestCase 要添加到由测试运行程序执行的套件中的实例。除了在中列出的模块中发现的测试外,还将运行这些额外的测试。 test_labels .

返回A TestSuite 实例已准备好运行。

DiscoverRunner.setup_databases(**kwargs)[源代码]

通过调用 setup_databases() .

DiscoverRunner.run_checks()[源代码]

运行 system checks .

DiscoverRunner.run_suite(suite, **kwargs)[源代码]

运行测试套件。

返回运行测试套件产生的结果。

DiscoverRunner.get_test_runner_kwargs()[源代码]

返回关键字参数以实例化 DiscoverRunner.test_runner 用。

DiscoverRunner.teardown_databases(old_config, **kwargs)[源代码]

销毁测试数据库,通过调用 teardown_databases() .

DiscoverRunner.teardown_test_environment(**kwargs)[源代码]

恢复预测试环境。

DiscoverRunner.suite_result(suite, result, **kwargs)[源代码]

基于测试套件和该测试套件的结果计算并返回返回代码。

测试实用程序

django.test.utils

为了帮助创建自己的测试运行程序,Django在 django.test.utils 模块。

setup_test_environment(debug=None)[源代码]

执行全局预测试设置,例如安装模板呈现系统的检测和设置虚拟电子邮件发件箱。

如果 debug 不是 None , the DEBUG 设置更新为其值。

teardown_test_environment()[源代码]

执行全局测试后拆卸,例如从模板系统中删除检测并恢复正常的电子邮件服务。

setup_databases(verbosity, interactive, keepdb=False, debug_sql=False, parallel=0, aliases=None, **kwargs)[源代码]

创建测试数据库。

返回提供足够详细信息以撤消所做更改的数据结构。此数据将提供给 teardown_databases() 测试结束时的功能。

这个 aliases 参数确定 DATABASES 应为设置别名测试数据库。如果未提供,则默认为 DATABASES 别名。

New in Django 2.2:

这个 aliases 已添加参数。

teardown_databases(old_config, parallel=0, keepdb=False)[源代码]

销毁测试数据库,恢复测试前条件。

old_config 是一个数据结构,用于定义需要反转的数据库配置中的更改。它是 setup_databases() 方法。

django.db.connection.creation

数据库后端的创建模块还提供了一些在测试过程中有用的实用程序。

create_test_db(verbosity=1, autoclobber=False, serialize=True, keepdb=False)

创建新的测试数据库并运行 migrate 反对它。

verbosity 具有与中相同的行为 run_tests() .

autoclobber 描述在发现与测试数据库同名的数据库时将发生的行为:

  • 如果 autoclobberFalse ,将要求用户批准销毁现有数据库。 sys.exit 如果用户不批准,则调用。
  • 如果autoclobber是 True ,数据库将在不咨询用户的情况下被销毁。

serialize 确定Django在运行测试之前是否将数据库序列化为内存中的JSON字符串(如果没有事务,则用于在测试之间还原数据库状态)。你可以把这个设置为 False 如果没有任何测试类, serialized_rollback=True .

如果您使用的是默认的测试运行程序,则可以使用 SERIALIZE 进入 TEST 字典。

keepdb 确定测试运行应使用现有数据库还是创建新数据库。如果 True 将使用现有数据库,如果不存在,则创建现有数据库。如果 False 将创建一个新数据库,提示用户删除现有数据库(如果存在)。

返回它创建的测试数据库的名称。

create_test_db() 具有修改的值的副作用 NAME 在里面 DATABASES 以匹配测试数据库的名称。

destroy_test_db(old_database_name, verbosity=1, keepdb=False)

销毁名称为值的数据库 NAME 在里面 DATABASES 和集合 NAME 的价值 old_database_name .

这个 verbosity 参数的行为与for相同 DiscoverRunner .

如果 keepdb 论证是 True ,则将关闭与数据库的连接,但不会破坏数据库。

与集成 coverage.py

代码覆盖率描述了测试了多少源代码。它显示代码的哪些部分正在测试中运行,哪些部分没有运行。它是测试应用程序的重要组成部分,因此强烈建议您检查测试的覆盖范围。

Django可以轻松地与 coverage.py 一种用于测量Python程序代码覆盖率的工具。第一, install coverage.py . 接下来,从包含 manage.py ::

coverage run --source='.' manage.py test myapp

这将运行测试并收集项目中已执行文件的覆盖率数据。您可以通过键入以下命令来查看此数据的报告:

coverage report

注意,一些django代码是在运行测试时执行的,但由于 source 传递给上一个命令的标志。

有关更多选项,如带批注的HTML列表,详细说明缺少的行,请参见 coverage.py 博士学位。