- 引言
- 本书涉及的内容
- 第 1 部分 Python 开发入门
- 第 1 章 Python 入门
- 第 2 章 开发 Web 应用
- 第 3 章 Python 项目的结构与包的创建
- 第 4 章 面向团队开发的工具
- 第 5 章 项目管理与审查
- 第 6 章 用 Mercurial 管理源码
- 第 7 章 完备文档的基础
- 第 8 章 模块分割设计与单元测试
- 第 9 章 Python 封装及其运用
- 第 10 章 用 Jenkins 持续集成
- 第 11 章 环境搭建与部署的自动化
- 第 12 章 应用的性能改善
- 第 13 章 让测试为我们服务
- 第 14 章 轻松使用 Django
- 第 15 章 方便好用的 Python 模块
- 附录 A VirtualBox 的设置
- 附录 B OS(Ubuntu)的设置
10.6 执行 Django 的测试
前面我们学习了涉及到 unittest 和 pytest 的测试代码的执行方法,这里我们学习一下如何执行 Django 的测试。Django 有着专用的测试机制,各位在采用 Django 时可以拿本节内容作为参考。在学习如何执行测试的同时,我们还会学习如何向邮箱、Slack 等发送通知。
10.6.1 安装 Python 模块
这里要安装 Django 主体程序以及 Django 与 Jenkins 进行联动时所需的几个工具。
◉ Django 主体程序
首先安装 Django 主体程序。
$ pip install django
◉ unittest-xml-reporting
我们知道,要想让 Jenkins 显示测试结果报告,需要将测试执行结果以 Junit 格式的 xml 文件形式输出。
使用 Django 的 manage.py test 命令执行测试后,生成的结果只是纯文本,并不满足上述格式要求。用 unittest-xml-reporting 可以解决这一问题,其安装如下。
$ pip install unittest-xml-reporting
◉ coverage
还要安装 coverage 模块。
$ pip install coverage
10.6.2 Django 的调整
◉ 创建项目目录
创建项目目录。这里我们用 cafe/apps 作为其名称。
$ mkdir cafe $ cd cafe $ django-admin.py startproject apps
这个项目由 Mercurial 的版本库管理,保存在 /var/hg/cafe 目录下。
◉ 创建 Django 应用
接下来创建 Django 应用。运行上面那条命令之后,我们会得到 apps 目录。现在移动到该目录下,创建一个名为 menu 的应用。
$ cd apps $ python manage.py startapp menu
执行完毕后将生成 apps/menu 目录。
10.6.3 示例代码
◉ 编写模型
编写模型。我们这里所用的概念是一间咖啡厅的茶品清单,代码如 LIST 10.4 所示。
LIST 10.4 cafe/apps/menu/models.py
# -*- coding:utf-8 -*- from django.db import models TEA_KINDS = ( ("english", u" 英式红茶"), ("chinese", u" 中国茶"), ("japanese", u" 日本茶"), ) class TeaManager(models.Manager): def recommended(self): """ 仅显示推荐商品 """ return self.filter(is_flavor=True) def count_each_kind(self): """ 以字典形式返回各类茶的件数 """ result = self.values_list("kind").annotate( count=models.Count("kind")) return dict(result) class Tea(models.Model): objects = TeaManager() name = models.CharField(u" 名称", max_length=255) kind = models.CharField(u" 种类", max_length=255, choices=TEA_KINDS) price = models.IntegerField(u" 价格") is_recommended = models.BooleanField( u" 推荐商品", default=False)
◉ 编写表单
表单代码如 LIST 10.5 所示。
LIST 10.5 cafe/apps/menu/forms.py
# -*- coding:utf-8 -*- from django import forms from menu.models import Tea, TEA_KINDS class TeaSearchForm(forms.Form): name = forms.CharField(label=u"", max_length=255, required=False) kind = forms.MultipleChoiceField( label=u" 种类", choices=TEA_KINDS, required=False) extra_report = forms.BooleanField( label=u" 输出追加报告", required=False)False) def clean(self): clnd = self.cleaned_data if not self.is_valid(): return clnd if not clnd["name"] and not clnd["kind"]: raise forms.ValidationError( u" 名称和种类请至少输入一项") return clnd
◉ 向 settings.INSTALLED_APPS 添加 menu
创建完 menu 应用后记得在 settings.py 的 INSTALLED_APPS 里指定它(LIST 10.6)。缺少这一步是不能用 manage.py 执行测试的。
LIST 10.6 cafe/apps/settings.py
INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'menu', )
◉ 为 Jenkins 写 settings
我们希望 Jenkins 利用 unittest-xml-reporting 将测试结果以 xml 文件的形式输出,所以这里要专门为 Jenkins 写一个 settings。
代码如 LIST 10.7 所示。
LIST 10.7 cafe/apps/settings_jenkins.py
# -*- coding:utf-8 -*- from settings import * ###################### # Xml test runner ###################### TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' TEST_OUTPUT_DIR = 'test_reports' DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': 'jenkins.db', 'USER': '', 'PASSWORD': '', 'HOST': '', 'PORT': '', } }
◉ 生成迁移文件
前面我们用示例代码创建了模型,所以这里还需要生成迁移文件。移动到 manage.py 文件所在的目录下执行下述命令,即可完成迁移文件的生成。
$ python manage.py makemigrations
◉ 编写测试代码
模型和表单的代码都已经写好,接下来该编写测试代码了。测试代码如 LIST 10.8 所示。
LIST 10.8 cafe/apps/menu/tests.py
# -*- coding:utf-8 -*- import unittest from django.test import TestCase as DjangoTest from menu.models import Tea from menu.forms import TeaSearchForm row = lambda L: (dict(zip(L[0], x)) for x in L[1:]) class TeaManagerTest(DjangoTest): def setUp(self): datas = ( ("name", "kind"), (u" 大吉岭", "english"), (u" 锡兰红茶", "english"), (u" 乌龙茶", "chinese"), (u" 铁观音", "chinese"), (u" 普洱茶", "chinese"), (u" 静冈茶", "japanese"), ) for data in row(datas): Tea.objects.create(price=100, **data) def test_count_each_kind(self): result = Tea.objects.count_each_kind() self.assertEqual(result, dict(english=2, chinese=3, japanese=1)) class TeaSearchFormTest(unittest.TestCase): def test_valid(self): """ 检查输入正常时是否会报错 """ params = dict(name="foo", kind=["english"]) form = TeaSearchForm(params) self.assertEqual(form.is_valid(), True, form.errors.as_text()) def test_either1(self): """ 检查名称和种类都无输入时是否会报错 """ params = dict() form = TeaSearchForm(params) self.assertEqual(form.is_valid(), False, form.errors.as_text()) def test_either2(self): """ 检查输入名称后是否会报错 """ params = dict(name="foo") form = TeaSearchForm(params) self.assertEqual(form.is_valid(), True, form.errors.as_text()) def test_either3(self): """ 检查输入种类后是否会报错 """ params = dict(kind=["english", "chinese"]) form = TeaSearchForm(params) self.assertEqual(form.is_valid(), True, form.errors.as_text())
10.6.4 Jenkins 的调整
◉ 新建 Job
点击 Jenkins 面板侧边栏的“新建”,创建 Job。
① 选择“构建一个自由风格的软件项目”
② 在“Item 名称”处输入 cafe(Job 名并没有硬性要求,这里只是特意选择了与项目名称相同的 cafe)
◉ “源码管理系统”的设置
这里使用 Mercurial。我们事先已经将 cafe 的版本库放在了 /var/hg/cafe 下,所以直接在 Jenkins 的设置界面作如下输入即可。
· 源码管理系统:Mercurial
· Repository URL :/var/hg/cafe
· Branch :default
· 源码库浏览器:Auto
◉ 在 Execute shell 中填写测试命令
把 Django 测试结束后输出覆盖率报告的命令写在“Execute shell”中。如果只是单纯地进行 Django 测试,可以用下述命令执行。
$ PYTHONPATH=apps python -m manage test menu --settings=settings_jenkins
通过 coverage 命令执行上述代码即可获取覆盖率。接下来,我们将上述代码改成下面这样。
$ PYTHONPATH=apps coverage run --source=apps -m manage test menu --settings=s ettings_jenkins && coverage xml -o coverage_reports/coverage.xml
然后,run 成功之后,执行 coverage xml 命令即可输出 xml 格式的报告。
这里我们指定了 --source=apps 选项。该选项可以将覆盖率统计对象限制在指定的目录下。在遇到这种 Django 项目目录与版本库路径不一致的情况时,指定该选项能有效防止读取无关代码。
专栏 不要直接在Django 的项目目录下执行测试
获取覆盖率时有一点需要注意,那就是不能直接在 Django 的项目目录下执行测试。使用 Django 时有很多工作是在 Django 的项目目录下进行(比如上面例子中移动到 apps 目录下并执行 manage.py test )。如果用上述方法统计覆盖率,那么一旦我们移动到 apps 并执行 manage.py test ,会连同 Django 本身一同测试并统计覆盖率,最终的报告会变成图 10.12 这个样子。因此,为了防止非测试对象混入覆盖率报告,必须对这一点强加注意。
图 10.12 混入多余代码之后的覆盖率
10.6.5 “构建后操作”选项卡的设置
“构建后操作”选项卡里设置的任务将在 Job 的构建处理执行完毕后执行。
Jenkins 读取测试结果报告与覆盖率报告的操作需要在这里进行设置。另外,执行 Job 失败的邮件通知也是在这里设置的。
◉ 读取测试结果报告
“ Publish JUnit test result report”中有“测试报告( XML)”一项,用于指定输出的 xml 文件。
这里允许使用通配符。unittest-xml-reporting 会针对每一个测试类输出一个 xml 文件,所以按照下述方法设置即可。另外,测试结果的输出目录要与 Jenkins 的 settings 中指定的目录一致。
· 测试报告(XML)
cafe/test_reports/*.xml
◉ 读取覆盖率报告
“Publish CoberturaCoverage Report”里有“Cobertura xml report pattern”,我们要在这里指定 coverage xml 命令输出的文件。
· Cobertura xml report pattern
cafe/coverage_reports/*.xml
◉ 查看结果
上述设置完成之后,我们就可以看到测试结果报告和覆盖率报告了,如图 10.13 和图 10.14。
图 10.13 测试结果报告
图 10.14 覆盖率报告
NOTE
输出代码覆盖率报告时可能会出现乱码,这是 Java 字体设置导致的。解决中文乱码的方法如下。
· Linux
配置环境变量 export JAVA_TOOL_OPTIONS="-Dfile.encoding=UTF-8"
· Windows
新建变量 JAVA_TOOL_OPTIONS ,value 值为-Dfile.encoding=UTF-8
重启 Jenkins 之后再执行一遍刚刚出现乱码的Job,可以看到乱码问题已经被解决。
◉ 邮件通知
“E-mail Notification”中有“Recipients”,我们要在这里指定通知接收方的邮箱地址,如图 10.15 所示。
图 10.15 设置收件人
除此之外,还要在侧边栏“系统管理”→“系统设置”里找出“Jenkins Location”,在“系统管理员邮件地址”中填写 Jenkins 用来发送邮件的邮箱地址,然后在“E-mail Notification”下的“SMTP 服务器”中设置 SMPT 服务器(图 10.16、图 10.17)。另外,如果要使用 Gmail 等需要认证的邮箱账户,需要点击“E-mail Notification”中的“高级”按钮,指定“用户名”“密码”等信息。
图 10.16 设置发件人的邮箱地址
图 10.17 设置发件人的邮箱服务器
在上述设置完成之后,Jenkins 会在 Job 执行失败时发送邮件,以通知开发者。
NOTE
安装插件后还可以向即将讲到的 Slack、IRC、Jabber、Growl 等发送通知。另外,安装 Email-ext plugin 之后还可以对邮件的主题、正文、通知设置等进行自定义。
◉ 向 Slack 发送通知
Slack 是一款可以跨平台使用的团队交流工具,还可以与许多第三方服务集成。Jenkins 可以借助 Slack Plugin 向 Slack 发送通知。Slack 的相关说明请参考 4.3 节。
首先进行 Slack 的设置。找出需要通知的频道或组,选择“Add a service integration”,添加“Jenkins CI”。接下来会跳转到“Post to Channel”界面,确认要通知的频道或组无误后,点击“Add Jenkins CI Integration”按钮,跳转至自定义界面。这里要先将“Step 3”中写的“TeamDomain”和“Integration Token”复制下来,因为后面设置 Jenkins 时会用到。Slack 发送通知时所用的名字和头像可以在这个界面修改。最后点击“Save Setting”完成 Slack 的设置。
接下来设置 Jenkins。在 Jenkins 的“系统管理”→“系统设置”中找到“Global Slack Notifier Settings”,然后作如下更改(图 10.18)。
① TeamDomain:设置 Slack 时复制的 TeamDomain
② Integration Token:设置 Slack 时复制的 Integration Token
③ Channel:Slack 的频道名或组名
④ Build Server URL:Jenkins 服务器的 URL
图 10.18 Global Slack Notifier Settings 示例
接着在 Job 的设置中找到“Slack Notifications”,勾选要通知的项目,最后在“构建后操作”处选择添加 Slack Notifications”即可(图 10.19)。
图 10.19 通知项目示例
现在再执行 Job 就会向 Slack 发送通知了(图 10.20)。
图 10.20 向 Slack 发送通知
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论