Django 测试(第 1 部分)——最佳实践和示例

2023-10-10

测试至关重要。如果没有正确测试您的代码,您将永远不会知道代码现在或将来当代码库发生变化时是否能正常工作。修复因代码库更改引起的问题可能会花费无数时间。更糟糕的是,您甚至可能根本不知道存在问题,直到您的最终用户抱怨为止,这显然不是您想要了解代码中断的方式。

进行适当的测试将帮助确保如果某个特定功能出现故障,您会知道。测试也使得调试破坏代码变得更加容易,从而节省时间和金钱。

过去,我确实因为没有针对旧代码库正确测试新功能而失去了工作机会。不要让这种事发生在你身上。认真对待测试。您将对您的代码更有信心,您的雇主也会对您更有信心。它本质上是一份保险单。

测试帮助您构建良好的代码、查找错误并编写文档。

在这篇文章中,我们将首先查看包括最佳实践的简短介绍,然后再查看一些示例。

免费奖金: 单击此处获取免费的 Django 学习资源指南 (PDF)向您展示了构建 Python + Django Web 应用程序时的提示和技巧以及要避免的常见陷阱。

Django 测试简介

测试类型

单元测试和集成测试是两种主要类型:

  • 单元测试是测试一项特定功能的孤立测试。
  • 集成测试同时,是更大规模的测试,重点关注用户行为并测试整个应用程序。换句话说,集成测试结合了不同的代码功能,以确保它们的行为正确。

专注于单元测试。写了很多这些。与集成测试相比,这些测试更容易编写和调试,并且您拥有的测试越多,需要的集成测试就越少。单元测试应该很快。我们将研究一些加速测试的技术。

也就是说,即使您已经覆盖了单元测试,集成测试有时仍然是必要的,因为集成测试可以帮助捕获代码回归.

一般来说,测试会导致成功(预期结果)、失败(意外结果)或错误。您不仅需要测试预期结果,还需要测试代码处理意外结果的能力。

最佳实践

  1. 如果它可能破裂,则应进行测试。这包括模型、视图、表单、模板、验证器等等。
  2. 每次测试通常应该只测试一项功能。
  3. 把事情简单化。您不想在其他测试之上编写测试。
  4. 每当代码从存储库中拉取或推送时以及在推送到生产环境之前在暂存环境中运行测试。
  5. When upgrading to a newer version of Django:
    • 本地升级,
    • 运行你的测试套件,
    • 修复错误,
    • PUSH 到存储库和暂存,然后
    • 在交付代码之前再次进行暂存测试。

结构

构建您的测试以适合您的项目。我倾向于将每个应用程序的所有测试放在测试.py根据我正在测试的内容(例如模型、视图、表单等)对测试进行文件和分组。

您还可以绕过(删除)测试.py完全归档并在每个应用程序中以这种方式构建您的测试:

└── app_name
    └── tests
        ├── __init__.py
        ├── test_forms.py
        ├── test_models.py
        └── test_views.py

最后,您可以创建一个单独的测试文件夹,它镜像整个项目结构,放置一个测试.py每个应用程序文件夹中的文件。

较大的项目应使用后一种结构。如果您知道较小的项目最终会扩展到更大的项目,那么最好也使用后两种结构之一。我喜欢第一个和第三个结构,因为我发现当每个应用程序都可以在一个脚本中查看时,为每个应用程序设计测试会更容易。

第三方包

使用以下包和库来帮助编写和运行测试套件:

  • django-webtest:使得编写符合最终用户体验的功能测试和断言变得更加容易。将这些测试与 Selenium 测试结合起来,以全面覆盖模板和视图。
  • 覆盖范围:用于衡量测试的有效性,显示测试覆盖的代码库的百分比。如果您刚刚开始设置单元测试,覆盖范围可以帮助提供有关应测试内容的建议。覆盖率还可以用于将测试变成游戏:例如,我尝试每天增加测试覆盖的代码百分比。
  • django-发现-runner:如果您以不同的方式组织测试(例如,在测试.py)。因此,如果您将测试组织到单独的文件夹中(如上面的示例所示),则可以使用 discovery-runner 来查找测试。
  • 工厂男孩,模特妈妈, 和嘲笑: 全部用来代替固定装置或用于填充测试所需数据的 ORM。夹具和 ORM 都可能很慢,并且在模型发生变化时都需要更新。

例子

在这个基本示例中,我们将测试:

  • 楷模,
  • 意见,
  • 表格,以及
  • API。

下载 Github 存储库这里跟随。

设置

安装覆盖范围并将其添加到您的 INSTALLED_APPS 中:

$ pip install coverage==3.6

运行覆盖范围:

$ coverage run manage.py test whatever -v 2

使用详细级别 2,-v 2,了解更多详情。您还可以使用以下命令立即测试整个 Django 项目:coverage run manage.py test -v 2.

构建报告以查看应该从哪里开始测试:

$ coverage html

打开django15/htmlcov/index.html查看报告的结果。滚动到报告底部。您可以跳过 virtualenv 文件夹中的所有行。切勿测试任何内置 Python 函数或库,因为它们已经过测试。您可以将 virtualenv 从文件夹中移出,以使报告运行后更清晰。

让我们从测试模型开始。

测试模型

在覆盖范围报告中,单击“任何/型号”的链接。您应该看到这个屏幕:

Django testing coverage

本质上,该报告表明我们应该测试条目的标题。简单的。

打开测试.py并添加以下代码:

from django.test import TestCase
from whatever.models import Whatever
from django.utils import timezone
from django.core.urlresolvers import reverse
from whatever.forms import WhateverForm

# models test
class WhateverTest(TestCase):

    def create_whatever(self, title="only a test", body="yes, this is only a test"):
        return Whatever.objects.create(title=title, body=body, created_at=timezone.now())

    def test_whatever_creation(self):
        w = self.create_whatever()
        self.assertTrue(isinstance(w, Whatever))
        self.assertEqual(w.__unicode__(), w.title)

这里发生了什么?我们本质上创建了一个Whatever对象并测试创建的标题是否与预期标题匹配 - 确实如此。

注意:确保您的函数名称以test_,这不仅是一个常见的约定,而且还可以让 django-discover-runner 可以找到测试。另外,编写测试all添加到模型的方法。

重新运行覆盖范围:

$ coverage run manage.py test whatever -v 2

您应该看到以下结果,表明测试已通过:

test_whatever_creation (whatever.tests.WhateverTest) ... ok

----------------------------------------------------------------------
Ran 1 test in 0.002s

OK

然后,如果您再次查看覆盖率报告,模型现在应该为 100%。

测试视图

测试视图有时可能很困难。我通常使用单元测试来检查状态代码,并使用 Selenium Webdriver 来测试 AJAX、Javascript 等。

将以下代码添加到中的WhateverTest类中测试.py:

# views (uses reverse)

    def test_whatever_list_view(self):
        w = self.create_whatever()
        url = reverse("whatever.views.whatever")
        resp = self.client.get(url)

        self.assertEqual(resp.status_code, 200)
        self.assertIn(w.title, resp.content)

这里我们从客户端获取 URL,将结果存储在变量中resp然后测试我们的断言。首先,我们测试响应代码是否为200,然后我们测试实际的响应返回。你应该得到 结果如下:

test_whatever_creation (whatever.tests.WhateverTest) ... ok
test_whatever_list_view (whatever.tests.WhateverTest) ... ok

----------------------------------------------------------------------
Ran 2 tests in 0.052s

OK

再次运行您的报告。您现在应该看到“whatever/views”的链接,显示以下结果:

Test coverage in Django line by line

您还可以编写测试来确保某些事情失败。例如,如果用户需要登录才能创建新对象,如果实际上创建对象失败,则测试将成功。

让我们看一下硒的快速测试:

# views (uses selenium)

import unittest
from selenium import webdriver

class TestSignup(unittest.TestCase):

    def setUp(self):
        self.driver = webdriver.Firefox()

    def test_signup_fire(self):
        self.driver.get("http://localhost:8000/add/")
        self.driver.find_element_by_id('id_title').send_keys("test title")
        self.driver.find_element_by_id('id_body').send_keys("test body")
        self.driver.find_element_by_id('submit').click()
        self.assertIn("http://localhost:8000/", self.driver.current_url)

    def tearDown(self):
        self.driver.quit

if __name__ == '__main__':
    unittest.main()

安装硒:

$ pip install selenium==2.33.0

运行测试。 Firefox 应加载(如果您已安装)并运行测试。然后我们断言提交时加载了正确的页面。您还可以检查以确保新对象已添加到数据库中。

测试表格

添加以下方法:

def test_valid_form(self):
    w = Whatever.objects.create(title='Foo', body='Bar')
    data = {'title': w.title, 'body': w.body,}
    form = WhateverForm(data=data)
    self.assertTrue(form.is_valid())

def test_invalid_form(self):
    w = Whatever.objects.create(title='Foo', body='')
    data = {'title': w.title, 'body': w.body,}
    form = WhateverForm(data=data)
    self.assertFalse(form.is_valid())

请注意我们如何从 JSON 生成表单数据。这是一个固定装置。

您现在应该已经通过了 5 项测试:

test_signup_fire (whatever.tests.TestSignup) ... ok
test_invalid_form (whatever.tests.WhateverTest) ... ok
test_valid_form (whatever.tests.WhateverTest) ... ok
test_whatever_creation (whatever.tests.WhateverTest) ... ok
test_whatever_list_view (whatever.tests.WhateverTest) ... ok

----------------------------------------------------------------------
Ran 5 tests in 12.753s

OK

您还可以编写测试来断言是否根据表单本身中的验证器显示特定错误消息。

测试 API

第一的,您可以访问API从这个网址:http://localhost:8000/api/whatever/?format=json。这是一个简单的设置,因此测试也相当简单。

安装lxml并解散XML:

$ pip install lxml==3.2.3
$ pip install defusedxml==0.4.1

添加以下测试用例:

from tastypie.test import ResourceTestCase

class EntryResourceTest(ResourceTestCase):

    def test_get_api_json(self):
        resp = self.api_client.get('/api/whatever/', format='json')
        self.assertValidJSONResponse(resp)

    def test_get_api_xml(self):
        resp = self.api_client.get('/api/whatever/', format='xml')
        self.assertValidXMLResponse(resp)

我们只是断言我们在每种情况下都会得到回应。

下次

在下一个教程中,我们将研究一个更复杂的示例,并使用 model_mommy 生成测试数据。同样,您可以从以下位置获取代码回购协议.

免费奖金: 单击此处获取免费的 Django 学习资源指南 (PDF)向您展示了构建 Python + Django Web 应用程序时的提示和技巧以及要避免的常见陷阱。

有什么要补充的吗?请在下面发表评论。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Django 测试(第 1 部分)——最佳实践和示例 的相关文章

随机推荐

  • 继承和组合:Python OOP 指南(摘要)

    你探索过Python 中的继承和组合 您了解了继承和组合创建的关系类型 您还完成了一系列练习来了解如何在 Python 中实现继承和组合 在本课程中 您学习了如何 使用继承来表达是一个两个类之间的关系 评估继承关系是否正确 在Python中
  • 自定义数据类型

    当你通过一个目的到print 它使用以下方法将其转换为字符串str 功能 您可以创建一个 str 自定义对象上的方法来更改输出内容 class Person def init self name age self name name sel
  • Python 内部函数的基础知识

    以下是一些资源 可提供有关本课程所涵盖主题的更多信息 在 Python 中递归思考 真正的Python教程 Python 中的递归 简介 真正的Python教程
  • Python 中的条件语句 (if/elif/else)

    在本分步课程中 您将学习如何在 Python 中使用条件 if 语句 逐步掌握 if 语句 并了解如何在程序中编写复杂的决策代码 参加测验 通过我们的交互式 Python 条件语句 测验来测试您的知识 完成后 您将收到一个分数 以便您可以跟
  • 设置您的 Python CI 项目

    在本视频中 您将学习如何设置用于持续集成 CI 的 Python 项目 因此 创建了 GitHub 存储库 克隆了项目并实现了一些基本的库功能 这是calculator py示例代码中使用的文件 calculator py Calculat
  • ChatterBot:使用 Python 构建聊天机器人

    目录 演示 项目概况 先决条件 第 1 步 使用 Python ChatterBot 创建聊天机器人 第 2 步 开始训练您的聊天机器人 第 3 步 导出 WhatsApp 聊天记录 第 4 步 清理您的聊天导出 第 5 步 使用自定义数据
  • 进行日期和时间算术

    Python 标准库提供了timedelta班级用于表演加减在一个datetime目的 第三方库dateutil有更有用的方法来进行数学计算
  • 运行 Python 脚本

    作为 Python 开发人员需要培养的最重要技能之一是能够运行 Python 脚本和代码 这将是您了解代码是否按计划运行的唯一方法 这甚至是了解您的代码是否有效的唯一方法 本分步课程将指导您完成一系列运行 Python 脚本的方法 具体取决
  • 第 2 节审查

    该视频总结了有关装饰器的第 2 部分 您现在知道如何 创建简单的装饰器并将它们应用到函数中 通过使用应用语法糖 装饰你的功能的符号 重用装饰器并将它们导入到您的程序中 用参数修饰函数 从修饰函数返回值 对Python对象进行内省以及如何使用
  • 通过真正的 Python Slack 社区提升您的技能

    目录 享受生活 保持好奇心 提出问题并尊重他人 找到提问的最佳渠道 Spend Some Time Composing Your Questions 总结您的问题 为您的问题提供背景信息 提供一个最小的可重复示例 提供追溯 请勿交叉发帖 尝
  • 处理时区

    Python 3 9 在时区方面引入了重大变化 添加了zoneinfo数据库 在本课程中 您将学习如何使用ZoneInfo类将时区信息添加到datetime目的 您还将探索不一致的命名标准造成的复杂性 如果您想了解有关圣诞岛 基里蒂马蒂示例
  • PyCon Africa 2019(回顾)

    目录 PyCon Africa 发生了什么 主会议 穆斯塔法 西塞 人工智能产生积极影响的潜力 Meili Triantafyllidi 在柏林 PyLadies 工作 6 年的经验教训 Candy Tricia Khohliwe 网络虚拟
  • 第 142 集:使用 Apache Airflow 协调大型和小型项目

    第 142 集 使用 Apache Airflow 协调大型和小型项目 真正的 Python 播客 2023 年 1 月 27 日54m RSS Apple Podcasts Google Podcasts Spotify More 播客瘾
  • Python 新闻:2023 年 3 月以来的新增内容

    目录 Python 3 12 0 Alpha 6 发布 Python 本地包目录上的 PEP 582 被拒绝 PyCascades 2023 在不列颠哥伦比亚省温哥华举行 PyCon US 2023 招募志愿者 PyPI 发布博客 2022
  • 使用Python或运算符

    Python 中有 3 个布尔运算符 and or 和not 使用它们 您可以测试条件并决定程序将采用哪个执行路径 在本教程中 您将深入了解 Pythonor运算符及其使用方法 在本课程结束时 您将学到 Python 如何or操作员工作 如
  • Python F 字符串:讨厌的细节

    您已经了解了很多有关 F 弦的知识以及它们为何如此出色 在本课程中 您将了解在使用以下内部 f 字符串时要记住哪些细节 引号 词典 牙套 反斜杠
  • Python mmap:使用内存映射进行文件 I/O

    这Python之禅有很多智慧可以提供 一个特别有用的想法是 应该有一种 最好只有一种 明显的方法来做到这一点 然而 在 Python 中完成大多数事情有多种方法 而且通常都有充分的理由 例如 有Python 读取文件的多种方法 包括很少使用
  • Python 图形用户界面编程

    Python 图形用户界面编程 学习路径 技能 图形用户界面 GUI Python支持多种GUI框架或工具包 从传统上与 Python 捆绑在一起的 Tkinter 到许多跨平台解决方案 例如 PyQT 或 wxPython 您可以将其作为
  • 第 78 集:通过图解故事学习 Python

    第 78 集 通过图解故事学习 Python 真正的 Python 播客 2021 年 9 月 17 日48m RSS Apple Podcasts Google Podcasts Spotify More 播客瘾君子 灰蒙蒙 袖珍铸件 投
  • Django 测试(第 1 部分)——最佳实践和示例

    目录 Intro to Testing in Django 测试类型 最佳实践 结构 第三方包 Examples 设置 测试模型 测试视图 测试表格 测试 API 下次 测试至关重要 如果没有正确测试您的代码 您将永远不会知道代码现在或将来