返回介绍

10.6 执行 Django 的测试

发布于 2024-01-21 17:11:03 字数 11171 浏览 0 评论 0 收藏 0

前面我们学习了涉及到 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文