这个 RequestFactory
与测试客户端共享相同的API。但是,与浏览器的行为不同,RequestFactory提供了一种生成请求实例的方法,该实例可以用作任何视图的第一个参数。这意味着您可以像测试任何其他函数一样测试视图函数——作为一个黑盒,使用完全已知的输入,测试特定的输出。
的API RequestFactory
是测试客户端API的一个稍微受限制的子集:
它只能访问HTTP方法 get()
, post()
, put()
, delete()
, head()
, options()
和 trace()
.
这些方法接受所有相同的参数 除了 对于 follow
. 因为这只是一个生产请求的工厂,所以由您来处理响应。
它不支持中间件。如果视图正常工作需要,会话和身份验证属性必须由测试本身提供。
这个 query_params
参数已添加。
以下是使用请求工厂的单元测试::
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)
RequestFactory
创建类似WSGI的请求。如果您想创建类似ASGI的请求,包括拥有正确的ASGI scope
,您可以改为使用 django.test.AsyncRequestFactory
。
此类直接与API兼容 RequestFactory
,唯一的区别是它回来了 ASGIRequest
实例而不是 WSGIRequest
实例.它的所有方法仍然是同步可调用的。
中的任意关键字参数 defaults
直接添加到ASGI作用域。
这个 query_params
参数已添加。
为了在请求/响应周期之外测试基于类的视图,您必须通过调用 setup()
实例化后。
例如,假设以下基于类的视图:
views.py
¶from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = "myapp/home.html"
def get_context_data(self, **kwargs):
kwargs["environment"] = "Production"
return super().get_context_data(**kwargs)
您可以直接测试 get_context_data()
方法,首先实例化视图,然后传递 request
至 setup()
,在继续测试代码之前:
tests.py
¶from django.test import RequestFactory, TestCase
from .views import HomeView
class HomePageTest(TestCase):
def test_environment_set_in_context(self):
request = RequestFactory().get("/")
view = HomeView()
view.setup(request)
context = view.get_context_data()
self.assertIn("environment", context)
这个 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/",
headers={"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
,由数据库别名描述 default
和 dbreplica
由别名描述 replica
. 如你所料, dbreplica
已由数据库管理员配置为的读取副本 dbprimary
,所以在正常活动中,任何写入 default
将出现在 replica
.
如果Django创建了两个独立的测试数据库,这将破坏任何预期会发生复制的测试。然而, replica
数据库已配置为测试镜像(使用 MIRROR
测试设置),表示正在测试, replica
应该像镜子一样 default
.
配置测试环境后,测试版本 replica
将要 not 被创造出来。相反,连接到 replica
将被重定向到指向 default
。因此,写入到 default
将出现在 replica
--而是因为它们实际上是同一个数据库,而不是因为这两个数据库之间存在数据复制。由于这取决于事务,因此测试必须使用 TransactionTestCase
而不是 TestCase
。
默认情况下,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
将首先创建数据库,因为它是唯一没有依赖关系的数据库别名。这个 default
和 clubs
下一步将创建别名(尽管不能保证此对的创建顺序),然后 hearts
最后 spades
.
如果在 DEPENDENCIES
定义,一个 ImproperlyConfigured
将引发异常。
TransactionTestCase
¶警告
此属性是一个私有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自己的测试套件中是必需的。
设置 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.test.testcases.SerializeMixin
以顺序运行它们。此mixin使用文件系统 lockfile
。
例如,您可以使用 __file__
确定同一文件中继承自的所有测试类 SerializeMixin
将顺序运行::
import os
from django.test import TestCase
from django.test.testcases import SerializeMixin
class ImageTestCaseMixin(SerializeMixin):
lockfile = __file__
def setUp(self):
self.filename = os.path.join(temp_storage_dir, "my_file.png")
self.file = create_file(self.filename)
class RemoveImageTests(ImageTestCaseMixin, TestCase):
def test_remove_image(self):
os.remove(self.filename)
self.assertFalse(os.path.exists(self.filename))
class ResizeImageTests(ImageTestCaseMixin, TestCase):
def test_resize_image(self):
resize_image(self.file, (48, 48))
self.assertEqual(get_image_size(self.file), (48, 48))
如果你在写 reusable application 您可能希望使用Django测试运行程序来运行自己的测试套件,从而从Django测试基础结构中获益。
一种常见的做法是 tests 应用程序代码旁边的目录,结构如下:
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测试行为。这种行为包括:
正在执行全局预测试设置。
在当前目录下的任何文件中查找名称与模式匹配的测试 test*.py
.
正在创建测试数据库。
运行 migrate
将模型和初始数据安装到测试数据库中。
运行 system checks .
运行找到的测试。
正在销毁测试数据库。
正在执行全局测试后拆卸。
如果您定义自己的测试运行程序类和点 TEST_RUNNER
在那节课上,Django将在每次运行时执行测试运行程序 ./manage.py test
. 这样,就可以使用任何可以从python代码执行的测试框架,或者修改django测试执行过程以满足您可能拥有的任何测试需求。
测试运行器是定义 run_tests()
方法。Django出货时配备了一台 DiscoverRunner
定义默认Django测试行为的类。此类定义了 run_tests()
入口点,外加一组由 run_tests()
来设置、执行和拆除测试套件。
DiscoverRunner
将在任何匹配的文件中搜索测试 pattern
.
top_level
可用于指定包含顶级python模块的目录。通常,Django可以自动解决这个问题,所以不需要指定这个选项。如果指定,则通常应该是包含 manage.py
文件。
verbosity
确定将打印到控制台的通知和调试信息的数量; 0
没有输出, 1
是正常输出,并且 2
是详细输出。
如果 interactive
是 True
,测试套件有权在执行测试套件时向用户请求指示。这种行为的一个例子是请求删除现有测试数据库的权限。如果 interactive
是 False
,测试套件必须能够在没有任何手动干预的情况下运行。
如果 failfast
是 True
,检测到第一个测试失败后,测试套件将停止运行。
如果 keepdb
是 True
测试套件将使用现有数据库,或者在必要时创建一个数据库。如果 False
将创建一个新数据库,提示用户删除现有数据库(如果存在)。
如果 reverse
是 True
,测试用例将以相反的顺序执行。这对于调试没有正确隔离且有副作用的测试可能很有用。 Grouping by test class 使用此选项时将保留。此选项可与一起使用 --shuffle
来颠倒特定随机种子的顺序。
debug_mode
指定 DEBUG
在运行测试之前,应将设置设置为。
parallel
指定进程数。如果 parallel
大于 1
,测试套件将在 parallel
流程。如果测试用例类少于配置的进程,Django将相应地减少进程的数量。每个进程都有自己的数据库。此选项需要第三方 tblib
包以正确显示回溯。
tags
可用于指定一组 tags for filtering tests 。可以与 exclude_tags
。
exclude_tags
可用于指定一组 tags for excluding tests 。可以与 tags
。
如果 debug_sql
是 True
,失败的测试用例将输出记录到 django.db.backends logger 以及追溯。如果 verbosity
是 2
,然后输出所有测试中的查询。
test_name_patterns
可用于指定一组模式,用于按名称过滤测试方法和类。
如果 pdb
是 True
,调试器 (pdb
或 ipdb
)将在每次测试错误或失败时产生。
如果 buffer
是 True
,通过测试的输出将被丢弃。
如果 enable_faulthandler
是 True
, faulthandler
将被启用。
如果 timing
是 True
,将显示测试时间,包括数据库设置和总运行时间。
如果 shuffle
是一个整数,则测试用例将在执行之前以随机顺序打乱,并使用该整数作为随机种子。如果 shuffle
是 None
,种子将随机生成。在这两种情况下,种子都将被记录并设置为 self.shuffle_seed
在运行测试之前。此选项可用于帮助检测未正确隔离的测试。 Grouping by test class 使用此选项时将保留。
logger
可以用来传递一个Python Logger object 。如果提供,记录器将用于记录消息,而不是打印到控制台。记录器对象将遵循其日志记录级别,而不是 verbosity
。
durations
将显示N个最慢测试用例的列表。将此选项设置为 0
将导致显示所有测试的持续时间。需要使用Python3.12+。
Django可能会不时地通过添加新参数来扩展测试运行程序的功能。这个 **kwargs
声明允许此扩展。如果你是子类 DiscoverRunner
或者编写自己的测试运行程序,确保它接受 **kwargs
.
测试运行程序还可以定义其他命令行选项。创建或重写 add_arguments(cls, parser)
类方法并通过调用 parser.add_argument()
在方法内部,这样 test
命令将能够使用这些参数。
用于构建测试套件的类。默认设置为 unittest.TestSuite
. 如果您希望实现不同的逻辑来收集测试,则可以覆盖此项。
这是低级测试运行程序的类,用于执行单个测试并格式化结果。默认设置为 unittest.TextTestRunner
. 尽管命名约定中存在不幸的相似性,但这与 DiscoverRunner
包括一系列更广泛的责任。可以重写此属性以修改运行和报告测试的方式。
这是一个类,它从测试用例或模块或其他地方加载测试,并将它们打包到测试套件中,供运行程序执行。默认设置为 unittest.defaultTestLoader
. 如果您的测试将以不寻常的方式加载,则可以重写此属性。
运行测试套件。
test_labels
允许您指定要运行的测试并支持多种格式(请参见 DiscoverRunner.build_suite()
获取支持格式的列表)。
此方法应返回失败的测试数。
重写此类方法以添加 test
管理命令。见 argparse.ArgumentParser.add_argument()
有关向分析器添加参数的详细信息。
通过调用设置测试环境 setup_test_environment()
设置 DEBUG
到 self.debug_mode
(默认为 False
)
构造与提供的测试标签匹配的测试套件。
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
(见上文)。
返回A TestSuite
实例已准备好运行。
通过调用 setup_databases()
.
运行 system checks 对测试 databases
。
销毁测试数据库,通过调用 teardown_databases()
.
如果一个 logger
,则以给定整数记录消息。 logging level (例如: logging.DEBUG
, logging.INFO
,或 logging.WARNING
)。否则,消息将打印到控制台,并与当前 verbosity
。例如,不会打印消息,如果 verbosity
为0, INFO
和以上将被打印,如果 verbosity
至少为1,并且 DEBUG
如果至少为2,则将被打印。 level
默认为 logging.INFO
。
django.test.utils
¶为了帮助创建自己的测试运行程序,Django在 django.test.utils
模块。
执行全局预测试设置,例如安装模板呈现系统的检测和设置虚拟电子邮件发件箱。
如果 debug
不是 None
, the DEBUG
设置更新为其值。
创建测试数据库。
返回提供足够详细信息以撤消所做更改的数据结构。此数据将提供给 teardown_databases()
测试结束时的功能。
这个 aliases
参数确定哪一个 DATABASES
应为其设置别名测试数据库。如果未提供,则默认为所有 DATABASES
别名。
这个 serialized_aliases
参数确定哪些子集 aliases
测试数据库应将其状态序列化,以允许使用 serialized_rollback 特写。如果未提供,则默认为 aliases
。
销毁测试数据库,恢复测试前条件。
old_config
是一个数据结构,用于定义需要反转的数据库配置中的更改。它是 setup_databases()
方法。
django.db.connection.creation
¶数据库后端的创建模块还提供了一些在测试过程中有用的实用程序。
创建新的测试数据库并运行 migrate
反对它。
verbosity
具有与中相同的行为 run_tests()
.
autoclobber
描述在发现与测试数据库同名的数据库时将发生的行为:
如果 autoclobber
是 False
,将要求用户批准销毁现有数据库。 sys.exit
如果用户不批准,则调用。
如果 autoclobber
是 True
,数据库将在未咨询用户的情况下被销毁。
serialize
确定Django在运行测试之前是否将数据库序列化为内存中的JSON字符串(如果没有事务,则用于在测试之间还原数据库状态)。你可以把这个设置为 False
如果没有任何测试类, serialized_rollback=True .
keepdb
确定测试运行应使用现有数据库还是创建新数据库。如果 True
将使用现有数据库,如果不存在,则创建现有数据库。如果 False
将创建一个新数据库,提示用户删除现有数据库(如果存在)。
返回它创建的测试数据库的名称。
销毁名称为值的数据库 NAME
在里面 DATABASES
和集合 NAME
的价值 old_database_name
.
这个 verbosity
参数的行为与for相同 DiscoverRunner
.
如果 keepdb
论证是 True
,则将关闭与数据库的连接,但不会破坏数据库。
coverage.py
¶代码覆盖率描述了测试了多少源代码。它显示代码的哪些部分正在测试中运行,哪些部分没有运行。它是测试应用程序的重要组成部分,因此强烈建议您检查测试的覆盖范围。
Django可以很容易地与 coverage.py ,这是一种用于测量Python程序的代码覆盖率的工具。首先,安装 coverage 。接下来,从包含以下内容的项目文件夹中运行以下命令 manage.py
:
coverage run --source='.' manage.py test myapp
这将运行您的测试并收集项目中已执行文件的覆盖率数据。您可以通过键入以下命令查看此数据的报告:
coverage report
注意,一些django代码是在运行测试时执行的,但由于 source
传递给上一个命令的标志。
有关更多选项,如带批注的HTML列表,详细说明缺少的行,请参见 coverage.py 博士学位。
7月 22, 2024