作者选择了开放互联网/自由言论基金作为为国家写作计划的一部分接受捐赠。
简介
要想建立的网站第一次就能完美运行而不出错,几乎是不可能的。因此,您需要对网络应用程序进行测试,以便发现这些错误并积极加以解决。为了提高测试的效率,通常会将测试分成若干单元,以测试网络应用程序的特定功能。这种做法称为单元测试。由于测试集中于项目的小部分(单元),与其他部分无关,因此更容易发现错误。
测试网站可能是一项复杂的任务,因为网站是由处理 HTTP 请求、表单验证和渲染模板等多层逻辑组成的。不过,Django 提供了一套工具,可以让你无缝地测试你的网络应用程序。在 Django 中,编写测试的首选方法是使用 Python unittest
模块,当然也可以使用其他测试框架。
在本教程中,您将在 Django 项目中建立一个测试套件,并为应用程序中的模型和视图编写单元测试。您将运行这些测试,分析其结果,并学习如何查找测试失败的原因。
先决条件
在开始本教程之前,您需要准备以下材料:
- 在服务器上安装 Django 并设置编程环境。为此,您可以参考我们的 如何安装 Django Web 框架并设置编程环境 教程。
- 创建一个包含模型和视图的 Django 项目。在本教程中,我们使用了 Django 开发 系列教程中的项目。
第 1 步 - 为 Django 应用程序添加测试套件
Django 中的测试套件是项目中所有应用程序中所有测试用例的集合。为了让 Django 测试工具发现您的测试用例,您需要将测试用例写入名称以 test
开头的脚本中。在此步骤中,您将为测试套件创建目录结构和文件,并在其中创建一个空测试用例。
如果您学习了 Django 开发 系列教程,您将拥有一个名为 blogsite
的 Django 应用程序。
让我们创建一个文件夹来存放所有测试脚本。首先,激活虚拟环境:
1cd ~/my_blog_app
2. env/bin/activate
然后导航到 blogsite
应用程序目录,即包含 models.py
和 views.py
文件的文件夹,然后新建一个名为 tests
的文件夹:
1cd ~/my_blog_app/blog/blogsite
2mkdir tests
接下来,你要把这个文件夹变成一个 Python 包,所以要添加一个 __init__.py
文件:
1cd ~/my_blog_app/blog/blogsite/tests
2touch __init__.py
现在,您将添加一个用于测试模型的文件和另一个用于测试视图的文件:
1touch test_models.py
2touch test_views.py
最后,您将在 test_models.py
中创建一个空测试用例。您需要导入 Django TestCase
类,并使其成为您自己的测试用例类的超类。稍后,您将为该测试用例添加方法,以测试模型中的逻辑。打开文件 test_models.py
:
1nano test_models.py
现在在文件中添加以下代码:
1[label ~/my_blog_app/blog/blogsite/tests/test_models.py]
2from django.test import TestCase
3
4class ModelsTestCase(TestCase):
5 pass
现在,您已成功地为 blogsite
应用程序添加了一个测试套件。接下来,您将填写在此创建的空模型测试用例的详细信息。
第 2 步 - 测试 Python 代码
在这一步中,您将测试在 models.py
文件中编写的代码的逻辑。特别是,您将测试 Post
模型的 save
方法,以确保它在调用时创建帖子标题的正确标签。
首先,让我们看看您的 models.py
文件中已有的 Post
模型的 save
方法代码:
1cd ~/my_blog_app/blog/blogsite
2nano models.py
您将看到以下内容:
1[label ~/my_blog_app/blog/blogsite/models.py]
2class Post(models.Model):
3 ...
4 def save(self, *args, **kwargs):
5 if not self.slug:
6 self.slug = slugify(self.title)
7 super(Post, self).save(*args, **kwargs)
8 ...
我们可以看到,它会检查即将保存的帖子是否有 slug 值,如果没有,则调用 slugify
为其创建 slug 值。您可能需要测试这种类型的逻辑,以确保在保存文章时确实创建了 slug。
关闭文件。
要测试这一点,请返回 test_models.py
:
1nano test_models.py
然后将其更新为以下内容,并添加突出显示的部分:
1[label ~/my_blog_app/blog/blogsite/tests/test_models.py]
2from django.test import TestCase
3from django.template.defaultfilters import slugify
4from blogsite.models import Post
5
6class ModelsTestCase(TestCase):
7 def test_post_has_slug(self):
8 """Posts are given slugs correctly when saving"""
9 post = Post.objects.create(title="My first post")
10
11 post.author = "John Doe"
12 post.save()
13 self.assertEqual(post.slug, slugify(post.title))
这个新方法 test_post_has_slug
创建了一个标题为 "我的第一篇文章"
的新文章,然后为文章指定了作者并保存了文章。然后,使用 Python unittest
模块中的 assertEqual
方法检查帖子的标签是否正确。assertEqual
方法会检查传递给它的两个参数是否相等(由 "=="
运算符决定),如果不相等,就会引发错误。
保存并退出 test_models.py
。
这是一个可以测试的示例。添加到项目中的逻辑越多,需要测试的内容就越多。如果您在 save
方法中添加了更多逻辑,或为 Post
模型创建了新方法,您就需要在这里添加更多测试。您可以将它们添加到 test_post_has_slug
方法或创建新的测试方法,但它们的名称必须以 test
开头。
您已成功地为 Post
模型创建了一个测试用例,在该测试用例中,您断言保存后会正确地创建连接词。下一步,您将编写一个测试用例来测试视图。
第 3 步 - 使用 Django 的测试客户端
在此步骤中,您将编写一个测试用例,使用 Django 测试客户端测试视图。测试客户端](https://docs.djangoproject.com/en/3.0/topics/testing/tools/) 是一个 Python 类,它可以充当一个虚拟的 Web 浏览器,允许您测试视图,并以与用户相同的方式与 Django 应用程序交互。您可以在测试方法中引用 self.client
来访问测试客户端。例如,让我们在 test_views.py
中创建一个测试用例。首先,打开 test_views.py
文件:
1nano test_views.py
然后添加以下内容:
1[label ~/my_blog_app/blog/blogsite/tests/test_views.py]
2from django.test import TestCase
3
4class ViewsTestCase(TestCase):
5 def test_index_loads_properly(self):
6 """The index page loads properly"""
7 response = self.client.get('your_server_ip:8000')
8 self.assertEqual(response.status_code, 200)
ViewsTestCase "包含一个 "test_index_loads_properly "方法,该方法使用 Django 测试客户端访问网站的索引页面("http://your_server_ip:8000",其中 "your_server_ip "是您使用的服务器的 IP 地址)。然后,测试方法会检查响应的状态代码是否为 200
,这意味着页面响应没有任何错误。因此,您可以确信,当用户访问时,它也会无错响应。
除了状态代码,您还可以在 Django 文档测试响应页面 阅读测试客户端响应的其他属性。
在这一步中,你创建了一个测试用例,用于测试渲染索引页面的视图是否能正常工作而不会出错。现在测试套件中有两个测试用例。下一步将运行它们,查看结果。
第 4 步 - 运行测试
现在,您已经为项目构建了一套测试,是时候执行这些测试并查看其结果了。要运行测试,请导航到 blog
文件夹(其中包含应用程序的 manage.py
文件):
1cd ~/my_blog_app/blog
然后用
1python manage.py test
您将在终端看到类似下面的输出:
1[secondary_label Output]
2Creating test database for alias 'default'...
3System check identified no issues (0 silenced).
4..
5----------------------------------------------------------------------
6Ran 2 tests in 0.007s
7
8OK
9Destroying test database for alias 'default'...
在此输出中,有两个点 ...
,每个点代表一个通过的测试用例。现在要修改 test_views.py
以触发一个失败的测试。首先用以下命令打开文件
1nano test_views.py
然后将突出显示的代码更改为
1[label ~/my_blog_app/blog/blogsite/tests/test_views.py]
2from django.test import TestCase
3
4class ViewsTestCase(TestCase):
5 def test_index_loads_properly(self):
6 """The index page loads properly"""
7 response = self.client.get('your_server_ip:8000')
8 self.assertEqual(response.status_code, 404)
在此,您已将状态代码从 200
更改为 404
。现在使用 manage.py
,再次从您的目录运行测试:
1python manage.py test
您将看到以下输出:
1[secondary_label Output]
2Creating test database for alias 'default'...
3System check identified no issues (0 silenced).
4.F
5======================================================================
6FAIL: test_index_loads_properly (blogsite.tests.test_views.ViewsTestCase)
7The index page loads properly
8----------------------------------------------------------------------
9Traceback (most recent call last):
10 File "~/my_blog_app/blog/blogsite/tests/test_views.py", line 8, in test_index_loads_properly
11 self.assertEqual(response.status_code, 404)
12AssertionError: 200 != 404
13
14----------------------------------------------------------------------
15Ran 2 tests in 0.007s
16
17FAILED (failures=1)
18Destroying test database for alias 'default'...
你会看到一条描述性的失败消息,告诉你失败的脚本、测试用例和方法。它还会告诉你失败的原因,在本例中状态代码不等于 404
,信息为 AssertionError: 200 != 404
。这里的 AssertionError
是在 test_views.py
文件中高亮显示的代码行引发的:
1[label ~/my_blog_app/blog/blogsite/tests/test_views.py]
2from django.test import TestCase
3
4class ViewsTestCase(TestCase):
5 def test_index_loads_properly(self):
6 """The index page loads properly"""
7 response = self.client.get('your_server_ip:8000')
8 self.assertEqual(response.status_code, 404)
它告诉你断言是错误的,也就是说,响应状态代码 (200
)与预期 (404
)不同。在失败消息之前,您可以看到两个点 ..
现在变成了 .F
,这说明第一个测试用例通过了,而第二个测试用例没有通过。
结论
在本教程中,您在 Django 项目中创建了测试套件,添加了测试用例以测试模型和视图逻辑,学习了如何运行测试,并分析了测试输出。下一步,您可以为不在 models.py
和 views.py
中的 Python 代码创建新的测试脚本。
以下是一些在使用 Django 构建和测试网站时可能会有所帮助的文章:
- Django 单元测试](https://docs.djangoproject.com/en/3.0/topics/testing/overview/) 文档
- 扩展 Django](https://www.digitalocean.com/community/tutorial_series/scaling-django)系列教程
您还可以查看我们的 Django 主题页面 了解更多教程和项目。