该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程

所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址。


本节将简要介绍Django的自动化测试相关内容。

一、自动化测试概述

什么是自动化测试

测试是一种例行的、不可缺失的工作,用于检查你的程序是否符合预期。

测试可以划分为不同的级别。一些测试可能专注于小细节(比如某一个模型的方法是否会返回预期的值?), 一些测试则专注于检查软件的整体运行是否正常(用户在对网站进行了一系列的输入后,是否返回了期望的结果?)。

测试可以分为手动测试和自动测试。手动测试很常见,有时候print一个变量内容,都可以看做是测试的一部分。手动测试往往很零碎、不成体系、不够完整、耗时费力、效率低下,测试结果也不一定准确。

自动化测试则是系统地较为完整地对程序进行测试,效率高,准确性高,并且大部分共同的测试工作会由系统来帮你完成。一旦你创建了一组自动化测试程序,当你修改了你的应用,你就可以用这组测试程序来检查你的代码是否仍然同预期的那样运行,而无需执行耗时的手动测试。

为什么需要测试?

大家都明白:

  • 测试可以节省你的时间
  • 测试不仅仅可以发现问题,还能防止问题
  • 测试使你的代码更受欢迎
  • 测试有助于团队合作

二、编写测试程序

Django是一个全面、完善、严谨的Web框架,当然不会缺少测试功能。

1.遇见BUG

很巧,在我们的投票应用中有一个小bug需要修改:在Question.was_published_recently()方法的返回值中,当Qeustion在最近的一天发布的时候返回True(这是正确的),然而当Question在未来的日期内发布的时候也返回True(这是错误的)。

我们可以在admin后台创建一个发布日期在未来的Question,然后在shell中验证这个bug:

>>> import datetime
>>> from django.utils import timezone
>>> from polls.models import Question
>>> # 创建一个发布日期在30天后的问卷
>>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
>>> # 测试一下返回值
>>> future_question.was_published_recently()
True

问题的核心在于我们允许创建在未来时间才发布的问卷,由于“未来”不等于“最近”,因此这显然是个bug。

2.创建一个测试来暴露这个bug

刚才我们是在shell中测试了这个bug,那如何通过自动化测试来发现这个bug呢?

通常,我们会把测试代码放在应用的tests.py文件中,测试系统将自动地从任何名字以test开头的文件中查找测试程序。每个app在创建的时候,都会自动创建一个tests.py文件,就像views.py等文件一样。

将下面的代码输入投票应用的polls/tests.py文件中:

import datetime
from django.utils import timezone
from django.test import TestCase
from .models import Question class QuestionMethodTests(TestCase):
def test_was_published_recently_with_future_question(self):
"""
在将来发布的问卷应该返回False
"""
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
self.assertIs(future_question.was_published_recently(), False)

我们在这里创建了一个django.test.TestCase的子类,它具有一个方法,该方法创建一个pub_date在未来的Question实例。最后我们检查was_published_recently()的输出,它应该是 False。

3.运行测试程序

在终端中,运行下面的命令,

$ python manage.py test polls

你将看到结果如下:

Creating test database for alias 'default'...
F
======================================================================
FAIL: test_was_published_recently_with_future_question (polls.tests.QuestionMethodTests)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/path/to/mysite/polls/tests.py", line 16, in test_was_published_recently_with_future_question
self.assertIs(future_question.was_published_recently(), False)
AssertionError: True is not False
----------------------------------------------------------------------
Ran 1 test in 0.001s
FAILED (failures=1)
Destroying test database for alias 'default'...

这其中都发生了些什么?:

  • python manage.py test polls命令会查找投票应用中所有的测试程序
  • 发现一个django.test.TestCase的子类
  • 为测试创建一个专用的数据库
  • 查找名字以test开头的测试方法
  • test_was_published_recently_with_future_question方法中,创建一个Question实例,该实例的pub_data字段的值是30天后的未来日期。
  • 然后利用assertIs()方法,它发现was_published_recently()返回了True,而不是我们希望的False。

最后,测试程序会通知我们哪个测试失败了,错误出现在哪一行。

整个测试用例基本上和Python内置的unittest非常相似,大家可以参考Python教程中测试相关的章节。

3.修复bug

我们已经知道了问题所在,现在可以去修复bug了。修改源代码,具体如下:

# polls/models.py

def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now

再次运行测试程序:

Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Destroying test database for alias 'default'...

可以看到bug已经没有了。

4.更加全面的测试

事实上,前面的测试用例还不够完整,为了使was_published_recently()方法更加可靠,我们在上面的测试类中再额外添加两个其它的方法,来更加全面地进行测试。

# polls/tests.py

def test_was_published_recently_with_old_question(self):
"""
只要是超过1天的问卷,返回False
"""
time = timezone.now() - datetime.timedelta(days=1, seconds=1)
old_question = Question(pub_date=time)
self.assertIs(old_question.was_published_recently(), False) def test_was_published_recently_with_recent_question(self):
"""
最近一天内的问卷,返回True
"""
time = timezone.now() - datetime.timedelta(hours=23, minutes=59, seconds=59)
recent_question = Question(pub_date=time)
self.assertIs(recent_question.was_published_recently(), True)

现在我们有三个测试来保证无论发布时间是在过去、现在还是未来Question.was_published_recently()都将返回正确的结果。

关于测试的内容,虽然很重要,但是对于刚入门者而言却不是最首要的知识点,等将主体内容掌握后,我们再回头来梳理这一部分。

Part 5:Django测试--Django从入门到精通系列教程的更多相关文章

  1. 办公软件Office PPT 2010视频教程从入门到精通系列教程(22课时)

    办公软件Office PPT 2010视频教程从入门到精通系列教程(22课时) 乔布斯的成功离不开美轮美奂的幻灯片效果,一个成功的商务人士.部门经理也少不了各种各样的PPT幻灯片.绿色资源网给你提供了 ...

  2. Django环境安装--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  3. Part 3:视图和模板--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  4. Part 4:表单和类视图--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  5. Part 7:自定义admin站点--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  6. 查询集API -- Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

  7. 第一章:模型层model layer -- Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. 题外话: Django的教程写到这里,就进入 ...

  8. Part 6:静态文件--Django从入门到精通系列教程

    该系列教程系个人原创,并完整发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. 前面我们编写了一个经过测试的投票应用,现在让 ...

  9. Django简介--Django从入门到精通系列教程

    该系列教程系个人原创,并同步发布在个人官网刘江的博客和教程 所有转载本文者,需在顶部显著位置注明原作者及www.liujiangblog.com官网地址. Python及Django学习QQ群:453 ...

随机推荐

  1. Android架构篇--MVP模式的介绍篇

    摘要: 在MVVM成熟之前MVP模式在Android上有被神化的趋势,笔者曾经在商业项目中从零开始大规模采用过MVP模式对项目进行开发.在使用MVP模式进行开发的时候发现项目的结构模式对开发是有一定的 ...

  2. git本地仓库关联多个remote,怎么用本地一个分支向不同remote不同分支推送代码

    我想这个问题,是大家关注的问题,这个问题,我非常关注. 背景:在公司开发项目,我们一般都要把项目推送到公司领导创建的一个远程仓库里边去,但是我们同时也有自己的小仓库,这样的话,如何方便的将我们的代码, ...

  3. (其他)最常用的15大Eclipse开发快捷键技巧

    转自CSDNJava我人生(陈磊兴)   原文出处 引言 做java开发的,经常会用Eclipse或者MyEclise集成开发环境,一些实用的Eclipse快捷键和使用技巧,可以在平常开发中节约出很多 ...

  4. python数据探索

    数据质量分析 脏数据包括:缺失值:异常值:不一致的值:重复数据及含有特殊符号的数据: 1.缺失值处理 统计缺失率,缺失数 2.异常值处理 (1)简单统计量分析 (2)3Q原则 正态分布情况下,小概率事 ...

  5. python自动发送测试报告(五)

    python实现自动发送邮件具体步骤参考笔者的另一篇博文,python实现邮件的发送 本次只展示发送附件的代码,MIMEApplication支持常用格式文档(.jpg..mp3.zip等)当做附件上 ...

  6. Unable to load DLL 'SQLite.Interop.dll': The specified module could not be found. (Exception from HRESULT: 0x8007007E)

    在主工程(ASP.NET WEB/WCF等)的NuGet里引用 System.Data.SQLite.Core 不仅仅是Service需要引用,主工程即使不直接使用SQLite的库,也需要引用. 若使 ...

  7. Android中SELinux的TE简介【转】

    转自:https://blog.csdn.net/murphykwu/article/details/52457667 selinux的概念如上一篇链接所示: http://www.cnblogs.c ...

  8. ccf--20160403---路径解析

    本题思路如下: 具体的细节如下:首先去掉字符串中重复出现的/,然后遇到..,就删除栈的最后一个元素,.忽略 下面是代码和题目: 问题描述 试题编号: 201604-3 试题名称: 路径解析 时间限制: ...

  9. row_number() over() 一句话概括,以及max()函数的一种查询分组中最大值的用法

    row_number() over(partition by col1 order by col2) 根据COL1分组可能会有多个组,每组组内根据COL2进行排序.每组内都有自动生成的序号,从1开始, ...

  10. vue实例详解

    Vue实例的构造函数 每个 Vue.js 应用都是通过构造函数 Vue 创建一个 Vue 的根实例 启动的 虽然没有完全遵循 MVVM 模式, Vue 的设计无疑受到了它的启发.因此在文档中经常会使用 ...