针对应用的测试架构
简介
测试的目的是什么? 这里介绍针对应用架构的测试设计方法。
一步步攀爬
1 测试数据的查询
我们可能需要测试你自己代码的所有方面,而不是作为Python或Django的一部分提供的任何库或功能。
例如,考虑下面定义的模型。
你不需要显式测试它,并且已经像在数据库中一样正确存储,因为这是 Django 定义的东西(当然在实践中你会不可避免地在开发过程中测试此功能)。
你也不需要测试它是否已经被验证为日期字段,因为这又是在 Django 中实现的东西。
比如
Author first_name last_name CharFie ld date_of_birth
但是,您应该检查用于标签的文本(名字、姓氏、出生日期、死亡)以及为文本分配的字段大小(100 个字符),因为这些是您设计的一部分,将来可能会被破坏/更改。
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('Died', null=True, blank=True)
def get_absolute_url(self):
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
return '%s, %s' % (self.last_name, self.first_name)
同样,您应该检查自定义方法并按要求运行,因为它们是您的代码/业务逻辑。
在这种情况下,你可以相信 Django方法已经正确实现,所以你正在测试的是关联的视图实际上已经被定义。
精明的读者可能会注意到,我们也希望将出生和死亡的日期限制在合理的值上,并检查死亡是否在出生后发生。
在 Django 中,这个约束将被添加到你的表单类中,尽管你可以为模型字段和模型验证器定义验证器,这些验证器只有在模型的方法调用时才在表单级别使用。
这需要 ,或者需要专门调用模型的方法,比如
clean(),ModelFormclean()。
考虑到这一点,让我们开始研究如何定义和运行测试。
1.1 简介架构
在我们详细介绍“测试什么”之前,让我们先简要了解一下测试的定义位置和方式。
Django 使用 unittest 模块的内置测试发现,它将在任何以模式 test*.py 命名的文件中发现当前工作目录下的测试。
只要您正确命名文件,您就可以使用您喜欢的任何结构。
我们建议您为测试代码创建一个模块,并为模型、视图、窗体和需要测试的任何其他类型的代码提供单独的文件。例如:
blog/
/tests/
__init__.py
test_models.py
test_forms.py
test_views.py
在 posts 项目中创建如上所示的文件结构。
init.py应该是一个空文件(这告诉 Python 该目录是一个包)。
您可以通过复制并重命名框架测试文件 /posts/tests.py 来创建三个测试文件。
架构文件 /blog/tests.py 是我们在构建 Django 主干网站时自动创建的。
将所有测试都放在里面是完全“合法的”,但是如果您测试得当,您很快就会得到一个非常大且难以管理的测试文件。
可以删除骨架文件,因为我们不需要它。
打开 /blog/tests/test_models.py。
该文件应导入,如下所示:django.test.TestCase。
from django.test import TestCase
在这里创建测试集.
通常,您将为要测试的每个 models/views/windwos添加一个测试类,并使用单独的方法来测试特定功能。
在其他情况下,您可能希望有一个单独的类来测试特定用例,其中包含测试该用例各个方面的单个测试函数(例如,一个用于测试模型字段是否经过正确验证的类,其中包含用于测试每个可能的失败案例的函数)。
同样,结构在很大程度上取决于您,但最好是保持一致。
将下面的测试类添加到文件底部。该类演示如何通过派生自TestCase 来构造测试用例类。
class PostsTestClass(TestCase):
@classmethod
def setUpTestData(cls):
print("setUpTestData: Run once to set up non-modified data for all class methods.")
pass
def setUp(self):
print("setUp: Run once for every test method to setup clean data.")
pass
def test_false_is_false(self):
print("Method: test_false_is_false.")
self.assertFalse(False)
def test_false_is_true(self):
print("Method: test_false_is_true.")
self.assertTrue(False)
def test_one_plus_one_equals_two(self):
print("Method: test_one_plus_one_equals_two.")
self.assertEqual(1 + 1, 2)
新类定义了两个可用于预测试配置的方法(例如,创建测试所需的任何模型或其他对象):
setUpTestData()在测试运行开始时为类级设置调用一次。您可以使用它来创建不会在任何测试方法中修改或更改的对象。
setUp()在每个测试函数之前调用,以设置测试可能修改的任何对象(每个测试函数都将获得这些对象的“全新”版本)。
测试类也有一个我们没有使用过的方法tearDown()。此方法对于数据库测试不是特别有用,因为基类TestCase会为您处理数据库拆解。
在这些下面,我们有许多测试方法,它们使用函数来测试条件是真、假还是相等 (AssertTrue,AssertFalse,AssertEqual)。
如果条件未按预期评估,则测试将失败并将错误报告给主机。
Assert是 unittest 提供的标准断言。框架中还有其他标准断言。
以及特定于 Django 的断言来测试视图是否重定向 (AssertEqualassertRedirects),以测试是否使用了特定模板 (AssertTrueAssertFalseassertTemplateUsed) 等。
您通常不应该在测试中包含 print() 函数,如上所示。
我们在这里这样做只是为了让您可以看到在控制台中调用设置函数的顺序(在下一节中)。
1.2 执行测试
运行所有测试的最简单方法是使用以下命令:
python3 manage.py test
这将发现当前目录下所有以模式 test*.py 命名的文件,并运行使用适当的基类定义的所有测试,
这里我们有许多测试文件,但目前只有 /blog/tests/test_models.py 包含任何测试。
默认情况下,测试将仅单独报告测试失败,然后是测试摘要。
如果您收到类似于以下内容的错误,这可能是因为默认情况下测试不运行 collectstatic,
并且您的应用正在使用需要它的存储类(有关详细信息,请参阅manifest_strict)。
有许多方法可以解决此问题 - 最简单的方法是在运行测试之前运行
collectstatic:ValueError: Missing staticfiles manifest entry...
python3 manage.py collectstatic
在本地库的根目录中运行测试。您应该会看到如下所示的输出。
python3 manage.py test
输出:
Creating test database for alias 'default'...
setUpTestData: Run once to set up non-modified data for all class methods.
setUp: Run once for every test method to setup clean data.
Method: test_false_is_false.
setUp: Run once for every test method to setup clean data.
Method: test_false_is_true.
setUp: Run once for every test method to setup clean data.
Method: test_one_plus_one_equals_two.
.
======================================================================
FAIL: test_false_is_true (blog.tests.tests_models.YourTestClass)
----------------------------------------------------------------------
Traceback (most recent call last):
File "D:\django_tmp\libt_2\blog\tests\tests_models.py", line 22, in test_false_is_true
self.assertTrue(False)
AssertionError: False is not true
----------------------------------------------------------------------
Ran 3 tests in 0.075s
FAILED (failures=1)
Destroying test database for alias 'default'...
在这里,我们看到我们有一个测试失败,我们可以确切地看到哪个函数失败以及为什么(这个失败是意料之中的,因为不是true或false!
从上面的测试输出中学到的最重要的事情是,如果您为对象和方法使用描述性/信息性名称,它会更有价值。
函数print()的输出显示如何为类调用一次方法,以及如何在每个方法之前调用setUpTestData()该方法。 同样,通常您不会将setUp()这种添加到测试中。
1.3 控制测试信息量
接下来的部分将介绍如何运行特定测试,以及如何控制测试显示的信息量。
- 更多测试信息
如果要获取有关测试运行的详细信息,可以更改详细程度。
例如,要列出测试成功和失败(以及有关如何设置测试数据库的一大堆信息),您可以将详细程度设置为“2”,如下所示:
python3 manage.py test --verbosity 2
允许的详细级别为 0、1、2 和 3,默认值为“1”。
- 加快测试执行速度
如果您的测试是独立的,则在多处理器计算机上,您可以通过并行运行它们来显著加快测试速度。 下面使用以下命令为每个可用内核运行一个测试过程。
这是可选的,您还可以指定要使用的特定数量的内核。–parallel autoauto
python3 manage.py test --parallel auto
- 运行特定测试
如果要运行测试的子集,可以通过指定包、模块、子类或方法的完整点路径来实现:TestCase
执行特定的测试集
python3 manage.py test blog.tests
执行特定的测试用例模块
python3 manage.py test blog.tests.test_models
执行特定的测试用例模块的测试类
python3 manage.py test blog.tests.test_models.YourTestClass
执行特定的测试用例模块的测试类的测试方法
python3 manage.py test blog.tests.test_models.BlogTestClass.test_one_plus_one_equals_two
2 小结
其他测试运行程序选项,django为测试运行程序提供了许多其他选项,
包括随机执行测试(–shuffle)、
在调试模式下运行测试(–debug-mode)以及使用 Python 记录器捕获结果的功能。
有关更多信息,请参阅最后的参考内容 Django 测试运行程序文档。
https://docs.djangoproject.com/en/4.0/ref/django-admin/#test
- 点赞
- 收藏
- 关注作者
评论(0)