django入门-测试-part5
尊重作者的劳动,转载请注明作者及原文地址 http://www.cnblogs.com/txwsqk/p/6515996.html
完全翻译自官方文档 https://docs.djangoproject.com/en/1.10/intro/tutorial05/
前面好几段文字都是介绍什么是自动化测试,我们为什么需要写测试用例,还有现在流行的"测试驱动开发",总之有如下好处:
- 节省你的时间
你修改了工程,设计好多模块,如果没有自动化测试,好吧,手动一个个测吧 - 测试不能发现问题,但是能阻止问题的发生
没有测试用例,那么你程序的行为就是不可预见的,即使这是你写的代码,你实际也不知道它内部是怎么运行的;有了测试用例,当某一个地方出问题,它就能指出这部分有问题,即使你自己压根没有意识到这里有问题 - 测试可以让你的代码更有吸引力
当你写了一段很牛[A-Z][1]的代码,但是别人并不想看,因为你没有测试用例,那么别人就认为你的代码是不可信的 - 测试让团队合作更愉快
小的应用可能只有一个开发者,但是大部分复杂的应用是一个团队一起开发的,测试用例可以防止你的同事不小心干扰了你的代码
好了,总之就是为你的工程写测试 百利无一害
在前面章节中我们是怎么验证代码的,通过django-admin的shell
>>> import datetime
>>> from django.utils import timezone
>>> from polls.models import Question
>>> # create a Question instance with pub_date 30 days in the future
>>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
>>> # was it published recently?
>>> future_question.was_published_recently()
True
好了下面我们用django自带的测试功能--自动化测试
在你的应用目录下有一个 test.py 就是它
django的自动化测试系统会在你的应用目录下找test开头的文件然后运行它
我们往test.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):
"""
was_published_recently() should return False for questions whose
pub_date is in the future.
"""
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(pub_date=time)
self.assertIs(future_question.was_published_recently(), False)
运行一下
python manage.py test polls
运行这个命令都发生了什么呢
这个类继承了TestCase, 它会创建一个测试用的数据库,这个类的方法都应该是test开头的方法
运行结果如下
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 , in test_was_published_recently_with_future_question
self.assertIs(future_question.was_published_recently(), False)
AssertionError: True is not False ----------------------------------------------------------------------
Ran test in .001s FAILED (failures=)
Destroying test database for alias 'default'...
显然测试失败了
我们创建了一个问题,这个问题的发布时间是30天后,那么我们显然希望was_published_recently()返回False
但是测试用例却返回了True,所以我们代码有bug,我们修复一下polls/models.py
def was_published_recently(self):
now = timezone.now()
return now - datetime.timedelta(days=1) <= self.pub_date <= now
在重新执行下
python manage.py test polls
好了这回测试通过了
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.001s OK
Destroying test database for alias 'default'...
现在我们修复了 was_published_recently()的bug,我们来加几个复杂的测试用例
def test_was_published_recently_with_old_question(self):
"""
was_published_recently() should return False for questions whose
pub_date is older than 1 day.
"""
time = timezone.now() - datetime.timedelta(days=30)
old_question = Question(pub_date=time)
self.assertIs(old_question.was_published_recently(), False) def test_was_published_recently_with_recent_question(self):
"""
was_published_recently() should return True for questions whose
pub_date is within the last day.
"""
time = timezone.now() - datetime.timedelta(hours=1)
recent_question = Question(pub_date=time)
self.assertIs(recent_question.was_published_recently(), True)
下面我们来测试视图函数
我们在创建question时,如果pub_date是未来的时间,那么按理说这不应该在页面显示的,就像京东明天12:00有抢购活动,那么在这个时间点之前是不应该让用户看到的
在继续讲之前,先介绍一个django的测试命令 django.test.Client, 它可以在视图层模拟用户的交互行为,我们可以用Client命令在test.py或shell中
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()
这个命令会加载模板渲染功能,这样我们就可以检验一些额外的属性比如response.context
注意这个命令不会去创建测试数据库,它用真实的数据库数据做测试
还有不要忘了在settings.py里设置正确的时区TIME_ZONE
Client怎么用呢
>>> from django.test import Client
>>> # create an instance of the client for our use
>>> client = Client()
>>> # get a response from '/'
>>> response = client.get('/')
>>> # we should expect a 404 from that address
>>> response.status_code
404
>>> # on the other hand we should expect to find something at '/polls/'
>>> # we'll use 'reverse()' rather than a hardcoded URL
>>> from django.urls import reverse
>>> response = client.get(reverse('polls:index'))
>>> response.status_code
200
>>> response.content
b'\n <ul>\n \n <li><a href="/polls/1/">What's up?</a></li>\n \n </ul>\n\n'
>>> # If the following doesn't work, you probably omitted the call to
>>> # setup_test_environment() described above
>>> response.context['latest_question_list']
<QuerySet [<Question: What's up?>]>
好了回到正题,修复我们的index视图,之前的index视图是这样的,没有判断question的发布时间是否是小于现在的时间
class IndexView(generic.ListView):
template_name = 'polls/index.html'
context_object_name = 'latest_question_list' def get_queryset(self):
"""Return the last five published questions."""
return Question.objects.order_by('-pub_date')[:5]
修正这个问题,当pub_date小于现在的时间时不显示
def get_queryset(self):
"""
Return the last five published questions (not including those set to be
published in the future).
"""
return Question.objects.filter(
pub_date__lte=timezone.now()
).order_by('-pub_date')[:5]
来测试一下这个新视图吧
def create_question(question_text, days):
"""
Creates a question with the given `question_text` and published the
given number of `days` offset to now (negative for questions published
in the past, positive for questions that have yet to be published).
"""
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, pub_date=time) class QuestionViewTests(TestCase):
def test_index_view_with_no_questions(self):
"""
If no questions exist, an appropriate message should be displayed.
"""
response = self.client.get(reverse('polls:index'))
self.assertEqual(response.status_code, 200)
self.assertContains(response, "No polls are available.")
self.assertQuerysetEqual(response.context['latest_question_list'], []) def test_index_view_with_a_past_question(self):
"""
Questions with a pub_date in the past should be displayed on the
index page.
"""
create_question(question_text="Past question.", days=-30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question.>']
) def test_index_view_with_a_future_question(self):
"""
Questions with a pub_date in the future should not be displayed on
the index page.
"""
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertContains(response, "No polls are available.")
self.assertQuerysetEqual(response.context['latest_question_list'], []) def test_index_view_with_future_question_and_past_question(self):
"""
Even if both past and future questions exist, only past questions
should be displayed.
"""
create_question(question_text="Past question.", days=-30)
create_question(question_text="Future question.", days=30)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question.>']
) def test_index_view_with_two_past_questions(self):
"""
The questions index page may display multiple questions.
"""
create_question(question_text="Past question 1.", days=-30)
create_question(question_text="Past question 2.", days=-5)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(
response.context['latest_question_list'],
['<Question: Past question 2.>', '<Question: Past question 1.>']
)
讲解:
create_question()函数让我们方便的创建question
上面的几个函数根据函数名就能知道是干什么的,不解释了
index视图修复完了,detail视图也得改
class DetailView(generic.DetailView):
...
def get_queryset(self):
"""
Excludes any questions that aren't published yet.
"""
return Question.objects.filter(pub_date__lte=timezone.now())
一样的逻辑,当pub_date less than equeal 小于等于timezone.now()现在的时间则不显示
为detail写测试用例
class QuestionIndexDetailTests(TestCase):
def test_detail_view_with_a_future_question(self):
"""
The detail view of a question with a pub_date in the future should
return a 404 not found.
"""
future_question = create_question(question_text='Future question.', days=5)
url = reverse('polls:detail', args=(future_question.id,))
response = self.client.get(url)
self.assertEqual(response.status_code, 404) def test_detail_view_with_a_past_question(self):
"""
The detail view of a question with a pub_date in the past should
display the question's text.
"""
past_question = create_question(question_text='Past Question.', days=-5)
url = reverse('polls:detail', args=(past_question.id,))
response = self.client.get(url)
self.assertContains(response, past_question.question_text)
继续总结
你会发现当你开始写测试用例,你的测试代码就疯狂的增加了,而且他们很多重复的内容,相比你写的代码,测试代码是那么的丑陋,这不要紧,因为大部分时间你都不会察觉到他们的存在,你可以继续你的开发任务,不过当你修改了视图逻辑,你也要同步更新你的测试用例,不然会有一大顿测试用例过不了
关于写测试用例,下面有几条好的建议:
- 一个模型或视图一个单独的测试类
- 每个测试的方法只测试一种情况
- 测试方法的名字要顾名思义
扩展一下
我们现在只介绍了基本的测试功能,当你想测试浏览器的行为时可以使用 "Selenium"测试框架,这个框架不光能测试你的代码,还有你的javascript,django也包含了一个工具LiveServerTestCase来让你使用Selenium
当你的功能很复杂时,你可能希望当你提交代码时能自动运行测试,那么你去关注一下"持续集成"相关的内容
现在很多ide可以检查你的代码覆盖率,用这个方法也能找出你的哪些代码还没有被测试到,这也能让你发现哪些代码已经没用了可以删掉了.
如果你发现有的代码无法被测试,很明显这段代码应该被重构或者删除.有个工具Coverage可以帮你识别无用的代码 参考https://docs.djangoproject.com/en/1.10/topics/testing/advanced/#topics-testing-code-coverage
django入门-测试-part5的更多相关文章
- django入门教程(下)
在两篇文章帮你入门Django(上)一文中,我们已经做了一个简单的小网站,实现了保存用户数据到数据库,以及从后台数据库读取数据显示到网页上这两个功能. 看上去没有什么问题了,不过我们可以让它变得更加完 ...
- Django 入门
Django 入门 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的软件设计模型,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以新闻内容 ...
- Django入门实践(三)
Django入门实践(三) Django简单应用 前面简单示例说明了views和Template的工作过程,但是Django最核心的是App,涉及到App则会和Model(数据库)打交道.下面举的例子 ...
- Django入门笔记
Django入门笔记 **文档包含Django安装包.学习的笔记.代码等 安装 Django参考附件,只需要把附件拷贝到你需要的目录就行.Django是1.8.16版本 Python:在附件中,其中有 ...
- python代码自动补全配置及Django入门Demo
django入门代码示例小博客:https://pan.baidu.com/s/1pLjLPSv 1.自动补全功能 许多人都知道 iPython 有很好的自动补全能力,但是就未必知道 python 也 ...
- python web框架Django入门
Django 简介 背景及介绍 Django是一个开放源代码的Web应用框架,由Python写成.采用了MVC的框架模式,即模型M,视图V和控制器C.它最初是被开发来用于管理劳伦斯出版集团旗下的一些以 ...
- Django入门第一步:构建一个简单的Django项目
Django入门第一步:构建一个简单的Django项目 1.简介 Django是一个功能完备的Python Web框架,可用于构建复杂的Web应用程序.在本文中,将通过示例跳入并学习Django.您将 ...
- 【django入门教程】Django的安装和入门
很多初学django的朋友,都不知道如何安装django开发以及django的入门,今天小编就给大家讲讲django入门教程. 注明:python版本为3.3.1.Django版本为1.5.1,操作系 ...
- python学习笔记--Django入门四 管理站点--二
接上一节 python学习笔记--Django入门四 管理站点 设置字段可选 编辑Book模块在email字段上加上blank=True,指定email字段为可选,代码如下: class Autho ...
随机推荐
- 使用javascript,jquery实现的图片轮播功能
使用javascript,jquery实现的图片轮播功能本功能采用最基础的javascript和一些简单的jquery技术实现,易理解,以修改使用,代码简易,适合刚开始接触到网站开发的朋友们参考.可以 ...
- Centos 7 安装和配置Redis
一. 安装 操作系统:Centos 7. 最小化安装 redis版本: 4.0.6 服务器地址:*** 第一步:下载redis安装包(如果有新的,下载最新的redis安装包) wget http:// ...
- radiobutton 选中的项不能去掉选择的问题
代码如下: RadioButton rbtn = new RadioButton(getApplicationContext()); rbtn.setText(String.valueOf(item. ...
- 如何用Word发布WordPress博客
目前大部分的博客作者在用Word写博客这件事情上都会遇到以下3个痛点: 1.所有博客平台关闭了文档发布接口,用户无法使用Word,Windows Live Writer等工具来发布博客.使用Word写 ...
- RocketMQ 运维指令
1.1. 控制台使用 RocketMQ 提供有控制台及一系列控制台命令,用于管理员对主题,集群,broker 等信息的管理 登录控制台 首先进入RocketMQ 工程,进入/RocketMQ/bin ...
- 支付宝 iOS SDK 官方下载页面[转]
from:http://blog.sina.com.cn/s/blog_6f72ff900102v0sw.html 藏得太深了,不得不记下来! 官方页面地址: https://b.alip ...
- CentOS 6.6 MySQL 8.0详细安装步骤
1.备份服务器上MySQL数据库 [root@localhost ] # mysqldump -h localhost -u root -proot --databases Surpass --rou ...
- Android-FileUtils工具类
文件相关工具类 public final class FileUtils { private FileUtils() { throw new UnsupportedOperationException ...
- python经典书记必读:Python编程快速上手 让繁琐工作自动化
所属网站分类: 资源下载 > python电子书 作者:熊猫烧香 链接:http://www.pythonheidong.com/blog/article/69/ 来源:python黑洞网,专注 ...
- Cockroachdb 三、副本设置
三 副本配置 CockroachDB 副本配置可分为三个等级,集群级别>数据库级别>表级别 格式 YAML range_min_bytes: <size-in-bytes> / ...