RequestFactory
与测试客户端共享相同的API。 但是,RequestFactory不是像浏览器一样工作,而是提供一种方法来生成可用作任何视图的第一个参数的请求实例。 这意味着您可以像测试任何其他函数一样测试视图函数 - 作为一个黑盒子,具有完全已知的输入,测试特定的输出。
RequestFactory
的API是测试客户端API的一个稍微受限的子集:
get()
,
post()
, put()
,
delete()
, head()
,
options()
, and trace()
.follows
. 因为这只是一个生产请求的工厂,所以由您来处理响应。以下是使用请求工厂的简单单元测试:
from django.contrib.auth.models import AnonymousUser, User
from django.test import TestCase, RequestFactory
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',
...
]
另一个选择是使用override_settings()
或modify_settings()
将所需的主机添加到ALLOWED_HOSTS
。 对于无法打包自己的设置文件的独立应用程序或域名列表不是静态的项目(例如,多租户的子域名),此选项可能更为可取。 例如,您可以为域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。
较旧的版本没有验证ALLOWED_HOSTS
,而测试这些技术是不必要的。
如果您使用主/副本(由某些数据库称为主/从属)测试多数据库配置,则创建测试数据库的这种策略会出现问题。 创建测试数据库时,不会有任何复制,因此,在主节点上创建的数据将不会在副本上看到。
为了弥补这一点,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
}
}
In this setup, we have two database servers: dbprimary
, described
by the database alias default
, and dbreplica
described by the
alias replica
. 如您所料,dbprimary
已由数据库管理员配置为default
的只读副本,因此在正常活动中,对replica
将显示在dbreplica
上。
如果Django创建了两个独立的测试数据库,这将破坏任何期望复制发生的测试。 但是,replica
数据库已被配置为测试镜(使用MIRROR
测试设置),表示在测试下,replica
应该被对待作为default
的镜像。
配置测试环境时,replica
的测试版本将不会创建。 相反,到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
数据库,因为它是唯一没有依赖关系的数据库别名。 The default
and
clubs
alias will be created next (although the order of creation of this
pair is not guaranteed), then hearts
, and finally spades
.
如果DEPENDENCIES
定义中有任何循环依赖关系,则会引发ImproperlyConfigured
异常。
TransactionTestCase
¶的高级功能TransactionTestCase。
available_apps T0> ¶ T1>
警告
此属性是专用API。 它可以在未来没有弃用期的情况下更改或删除,例如以适应应用程序加载的更改。
它用于优化Django自己的测试套件,其中包含数百个模型,但在不同应用程序中的模型之间没有关系。
默认情况下,available_apps
设置为None
。 每次测试后,Django调用flush
以重置数据库状态。 这将清空所有表并发出post_migrate
信号,该信号会为每个模型重新创建一个内容类型和三个权限。 此操作与模型的数量成比例地变得昂贵。
将available_apps
设置为应用程序列表指示Django表现为只有这些应用程序中的模型可用。 TransactionTestCase
的行为更改如下:
post_migrate
在每次测试之前触发,以便为可用应用中的每个模型创建内容类型和权限(如果缺少)。post_migrate
未触发;在选择正确的应用程序集之后,它将被下一个TransactionTestCase
触发。由于数据库未完全刷新,如果测试创建不包含在available_apps
中的模型实例,则它们将泄漏,并且可能导致无关的测试失败。 小心使用会话的测试;默认会话引擎将它们存储在数据库中。
由于冲洗数据库后不会发出post_migrate
,因此在TransactionTestCase
之后的状态与TestCase
后的状态不同:由侦听器创建的行post_migrate
。 考虑order in which tests are executed,这不是问题,只要给定测试套件中的TransactionTestCase
声明available_apps
没有一个。
available_apps
在Django自己的测试套件中是必需的。
TransactionTestCase。
reset_sequences T0> ¶ T1>
在TransactionTestCase
上设置reset_sequences = True测试运行:
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
会降低测试速度,因为主键重置是一个相对昂贵的数据库操作。
如果您正在编写reusable application,您可能需要使用Django测试运行器来运行自己的测试套件,从而从Django测试基础架构中受益。
通常的做法是应用程序代码旁边的tests目录,具有以下结构:
runtests.py
polls/
__init__.py
models.py
...
tests/
__init__.py
models.py
test_settings.py
tests.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测试运行器所需的最低限度。 您可能需要添加用于控制详细程度的命令行选项,传递特定测试标签以运行等。
SECRET_KEY = 'fake-key'
INSTALLED_APPS = [
"tests",
]
此文件包含运行应用测试所需的Django settings。
再次,这是一个最小的例子;您的测试可能需要额外的设置才能运行。
由于在运行测试时,测试包包含在INSTALLED_APPS
中,因此您可以在其models.py
文件中定义纯测试模型。
显然,unittest
不是唯一的Python测试框架。 虽然Django不为替代框架提供显式支持,但它提供了一种方法来调用为替代框架构建的测试,就像他们是正常的Django测试一样。
当您运行./ manage.py 测试
时,Django会查看TEST_RUNNER
设置来确定要做什么。 默认情况下,TEST_RUNNER
指向'django.test.runner.DiscoverRunner'
。 这个类定义了默认的Django测试行为。 这种行为包括:
test*.py
匹配的当前目录下的任何文件中查找测试。migrate
将模型和初始数据安装到测试数据库中。运行系统检查已添加。
如果您在该类中定义了自己的测试运行器类并指向TEST_RUNNER
,则Django将在您运行./ manage.py 测试时运行测试
。 这样,可以使用可以从Python代码执行的任何测试框架,或者修改Django测试执行过程以满足您可能遇到的任何测试需求。
测试运行器是定义run_tests()
方法的类。 Django附带一个定义默认Django测试行为的DiscoverRunner
类。 此类定义了run_tests()
入口点,以及run_tests()
用来设置,执行和删除测试套件的其他方法。
DiscoverRunner
(pattern ='test * .py',top_level = None,verbosity = 1,interactive = True,failfast = False,keepdb = False,reverse = False,debug_mode = False t8 >,debug_sql = False,** kwargs)[source] ¶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。
debug_mode
指定在运行测试之前应该设置DEBUG
设置。
如果True
为debug_sql
,失败的测试用例将输出记录到django.db.backends logger的SQL查询以及回溯。 如果verbosity
是2
,则输出所有测试中的查询。
Django可以不时地通过添加新的参数来扩展测试运行器的能力。 **kwargs
声明允许此扩展。 如果您子类化DiscoverRunner
或编写自己的测试运行器,请确保接受**kwargs
。
您的测试运行程序还可以定义其他命令行选项。
Create or override an add_arguments(cls, parser)
class method and add
custom arguments by calling parser.add_argument()
inside the method, so
that the test
command will be able to use those arguments.
添加了debug_mode
关键字参数。
DiscoverRunner。
test_suite T0> ¶ T1>
用于构建测试套件的类。 默认情况下,它设置为unittest.TestSuite
。 如果您希望实现不同的收集测试的逻辑,这可以被覆盖。
DiscoverRunner。
test_runner T0> ¶ T1>
这是用于执行单独测试和格式化结果的低级测试运行器的类。 默认情况下,它设置为unittest.TextTestRunner
。 尽管在命名约定中存在不幸的相似性,但这不是与DiscoverRunner
相同类型的类,它涵盖了更广泛的职责。 您可以覆盖此属性以修改运行和报告测试的方式。
DiscoverRunner。
test_loader T0> ¶ T1>
这是加载测试的类,无论是从TestCases或模块或其他方式,并将它们捆绑到测试套件中为跑步者执行。
默认情况下,它设置为unittest.defaultTestLoader
。 如果您的测试将以不寻常的方式加载,您可以覆盖此属性。
DiscoverRunner。
run_tests
(test_labels,extra_tests = None,** kwargs)[source] ¶ T6>运行测试套件。
test_labels
允许您指定要运行的测试并支持多种格式(有关支持的格式的列表,请参阅DiscoverRunner.build_suite()
)。
extra_tests
是要添加到测试运行程序执行的套件中的额外TestCase
实例的列表。 除了在test_labels
中列出的模块中发现的那些测试之外,还运行这些额外的测试。
此方法应返回失败的测试数。
DiscoverRunner。
add_arguments
(parser)[source]¶覆盖此类方法以添加由test
管理命令接受的自定义参数。 有关将参数添加到解析器的详细信息,请参见argparse.ArgumentParser.add_argument()
。
DiscoverRunner。
setup_test_environment
(**kwargs)[source]¶通过调用setup_test_environment()
并将DEBUG
设置为self.debug_mode
(默认为False
)来设置测试环境) 。
DiscoverRunner。
build_suite
(test_labels,extra_tests = None,** kwargs)[source] ¶ T6>构造与提供的测试标签匹配的测试套件。
test_labels
是描述要运行的测试的字符串列表。 测试标签可以采用以下四种形式之一:
path.to.test_module.TestCase.test_method
- 在测试用例中运行单个测试方法。path.to.test_module.TestCase
- 在测试用例中运行所有测试方法。path.to.module
- 在命名的Python包或模块中搜索并运行所有测试。path/to/directory
- 搜索并运行命名目录下的所有测试。如果None
的值为pattern
,则测试运行器将在名称与其test_labels
匹配的当前目录下的所有文件中搜索测试以上)。
extra_tests
是要添加到测试运行程序执行的套件中的额外TestCase
实例的列表。 除了在test_labels
中列出的模块中发现的那些测试之外,还运行这些额外的测试。
返回准备运行的TestSuite
实例。
DiscoverRunner。
setup_databases
(**kwargs)[source]¶通过调用setup_databases()
创建测试数据库。
DiscoverRunner。
get_test_runner_kwargs
()[source]¶返回用于实例化DiscoverRunner.test_runner
的关键字参数。
DiscoverRunner。
teardown_databases
(old_config,** kwargs)[source] ¶破坏测试数据库,通过调用teardown_databases()
来恢复测试前的条件。
django.test.utils
¶为了帮助创建自己的测试运行器,Django在django.test.utils
模块中提供了一些实用程序方法。
setup_test_environment
(debug=None)[source]¶执行全局预测试设置,例如为模板渲染系统安装仪器,并设置虚拟电子邮件发件箱。
如果debug
不是None
,则将DEBUG
设置更新为其值。
添加了debug
参数。
setup_databases
(verbosity, interactive, keepdb=False, debug_sql=False, parallel=0, **kwargs)[source]¶创建测试数据库。
返回一个数据结构,提供足够的详细信息来撤销已做的更改。 在测试结束时,此数据将提供给teardown_databases()
函数。
teardown_databases
(old_config,parallel = 0,keepdb = False)[source] ¶ T6>销毁测试数据库,恢复预测试条件。
old_config
是定义数据库配置中需要反转的更改的数据结构。 这是setup_databases()
方法的返回值。
django.db.connection.creation
¶数据库后端的创建模块还提供了一些在测试期间可用的实用程序。
create_test_db
(verbosity = 1,autoclobber = False,serialize = True,keepdb = T4>)¶ T5>创建新的测试数据库,并对其运行migrate
。
verbosity
具有与run_tests()
中相同的行为。
autoclobber
描述了如果发现与测试数据库具有相同名称的数据库,则会发生的行为:
autoclobber
是False
,则会要求用户批准销毁现有数据库。 如果用户不批准,则调用sys.exit
。True
,则数据库将被销毁,而无需咨询用户。serialize
确定Django在运行测试之前是否将数据库序列化为内存中的JSON字符串(用于在没有事务的情况下在测试之间恢复数据库状态)。 如果您没有任何具有serialized_rollback=True的测试类,您可以将其设置为False
以加快创建时间。
如果使用默认的测试运行器,则可以使用TEST
字典中的SERIALIZE
条目来控制。
keepdb
确定测试运行是否应使用现有数据库,或创建一个新的数据库。 如果True
,则将使用或创建现有数据库(如果不存在)。 如果False
,将创建一个新数据库,提示用户删除现有的数据(如果存在)。
返回它创建的测试数据库的名称。
destroy_test_db
(old_database_name,verbosity = 1,keepdb = False)¶销毁在DATABASES
中名称为NAME
的值的数据库,并将NAME
设置为old_database_name
的值。
verbosity
参数与DiscoverRunner
具有相同的行为。
如果keepdb
参数为True
,则与数据库的连接将关闭,但数据库不会被销毁。
coverage.py
¶集成代码覆盖率描述了已经测试了多少源代码。 它显示你的代码的哪些部分是由测试和哪些不是。 它是测试应用程序的重要组成部分,因此强烈建议检查测试的覆盖率。
Django可以轻松地与coverage.py集成,这是一个用于测量Python程序代码覆盖率的工具。 首先,安装coverage.py。 接下来,从包含manage.py
的项目文件夹中运行以下命令:
coverage run --source='.' manage.py test myapp
这将运行您的测试并收集项目中已执行文件的coverage数据。 您可以通过键入以下命令查看此数据的报告:
coverage report
请注意,一些Django代码在运行测试时已执行,但由于传递给上一个命令的source
标志,因此未在此处列出。
有关更多选项(如已注释的HTML列表,详细说明错过的行),请参阅coverage.py文档。
2017年9月6日