frist Django app — 五、Test
Test——很重要但是没有被重视起来的一个环节,至少是我自己,其实自己之前在做java web的时候就去尝试过怎么做REST接口的测试,一直没有找到一种合适方式,而且因为时间紧没有进一步深究,但是造成的后果每次做了修改之后都测试不充分,引起新的问题,所以这次对于python正好看看Django的单元测试。
用的是单独的数据库,数据库是干净的(暂未有数据库,test所有操作都是从零开始),不会对正式的数据库造成影响
Test Model
到现在我们主要的业务逻辑代码在model和view里面,所以我们的测试也主要是针对model和view。在Django中我们的测试代码写在tests.py里面,这里我们先在models.py的Question类里面添加一个was_published_recently方法:
def was_published_recently(self):
now = timezone.now()
return self.publ_date >= now - datetime.timedelta(days=1)
接下来针对这个方法写单元测试
class QuestionMethodTest(TestCase):
def test_was_pblished_recently_with_future_question(self):
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(publ_date=time)
self.assertEqual(future_question.was_published_recently(), False)
def test_was_pblished_recently_with_old_question(self):
time = timezone.now() - datetime.timedelta(days=30)
future_question = Question(publ_date=time)
self.assertEqual(future_question.was_published_recently(), False)
def test_was_pblished_recently_with_recently_question(self):
time = timezone.now() - datetime.timedelta(hours=1)
future_question = Question(publ_date=time)
self.assertEqual(future_question.was_published_recently(), True)
这里先写了三个测试用例,分别测试
- 时间大于当前时间的是否会被查到
- 时间小于当前时间某些天的question是否会被查询到
- 时间小于当前时间一天内(我们之前的“最近”的规则设置的就是一天)是否会被查询到
我们再看看Django为我们的单元测试提供了怎样的环境。
- 所有的测试继承自django.test.TestCase,TestCase提供了很多测试方法,比如:assertEqual,assertContains等
- django会查找所有以test开头的方法(又一个约定大于配置)
- 使用python manage.py test polls来运行我们的测试,可以只对某一个app运行测试
- 每次测试进行的时候,django会创建新的数据库,测试完成之后会删除数据库,这样保证每次测试不会有污染数据
我们在mysite目录里面运行测试
python manage.py test polls
可以看到输出
Creating test database for alias 'default'...
F..
======================================================================
FAIL: test_was_pblished_recently_with_future_question (polls.tests.QuestionMethodTest)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/root/django/mysite/polls/tests.py", line 17, in test_was_pblished_recently_with_future_question
self.assertEqual(future_question.was_published_recently(), False)
AssertionError: True != False ----------------------------------------------------------------------
Ran 3 tests in 0.001s FAILED (failures=1)
Destroying test database for alias 'default'...
可以看到总共三个测试,失败1个,查看失败信息发现返回的是true,和我们预期的不符,说明我们的was_published_recently函数的逻辑不正确,所有时间大于当前时间的应该不被查询出来,我们修正如下
def was_published_recently(self):
now = timezone.now()
return now >= self.publ_date >= now - datetime.timedelta(days=1)
再次运行就会发现三个均成功,结果是OK
Creating test database for alias 'default'...
...
----------------------------------------------------------------------
Ran 3 tests in 0.001s OK
Destroying test database for alias 'default'...
Test View
view层会调用model层的代码实现业务逻辑,我们通过上面model的测试保证了model层的正确性,接下来可以借用django提供的环境测试我们的业务逻辑是否正确,编辑tests.py
from django.test import TestCase
import datetime
from django.utils import timezone
from polls.models import Question
from django.core.urlresolvers import reverse # Create your tests here. def create_question(question_text, days):
time = timezone.now() + datetime.timedelta(days=days)
return Question.objects.create(question_text=question_text, publ_date=time) class QuestionMethodTest(TestCase):
def test_was_pblished_recently_with_future_question(self):
time = timezone.now() + datetime.timedelta(days=30)
future_question = Question(publ_date=time)
self.assertEqual(future_question.was_published_recently(), False)
def test_was_pblished_recently_with_old_question(self):
time = timezone.now() - datetime.timedelta(days=30)
future_question = Question(publ_date=time)
self.assertEqual(future_question.was_published_recently(), False)
def test_was_pblished_recently_with_recently_question(self):
time = timezone.now() - datetime.timedelta(hours=1)
future_question = Question(publ_date=time)
self.assertEqual(future_question.was_published_recently(), True) class QuestionViewTest(TestCase):
def test_index_view_with_no_questions(self):
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):
create_question('Past question.', -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):
create_question('Future question.', 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):
create_question('Past question.', -30)
create_question('Future question.', 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_question(self):
create_question('Past question 1.', -30)
create_question('Past question 2.', -5)
response = self.client.get(reverse('polls:index'))
self.assertQuerysetEqual(response.context['latest_question_list'], ['<Question: Past question 2.>', '<Question: Past question 1.>'])
django为我们的view层测试提供了更多的帮助
- TestCase提供的包含一个client,表示一个客户端
- 可以通过client调用get,post方法获取服务器的返回值response
- 获取response的HttpCode,返回的context参数
关于测试
- more is better,test will look after themselves。测试用例越多,测试越全面,如果代码修改了,测试用例执行就会失败,就可以提醒我们去修改相应的测试
- 一个单独的model或者view应该使用一个单独的test类
- 每一种测试情况写单独的测试方法
- 测试方法尽量描述它的功能(见文知意)
完整代码
http://pan.baidu.com/s/1geJ7DYj
frist Django app — 五、Test的更多相关文章
- frist Django app — 三、 View
前面已经说过了Django中model的一些用法,包括orm,以及操作的api,接下来就是搭一些简单的界面学习view——Django中的view.主要介绍以下两个方面: url映射 请求处理 模板文 ...
- frist Django app — 四、 完善View
上一篇已经完成了polls的基本功能,接下来完善剩下的vote功能和并使用generic views改进请求处理view.包含表单的简单运用和前后台参数传递. 目录 vote:完善投票功能 gener ...
- frist Django app — 一、 创建工程
缘起 既然python都学了,学习python的时候感觉是相见恨晚,一种新的编程语言带给我一种新的思考问题的方式,为了巩固学过的东西并进一步学习python,就想学学Django,看看会不会带给我关于 ...
- frist Django app— 二、 Model和管理界面
Django是符合MVC架构的,这里现学习M—Model,而且Django自带了一个管理model(数据库)的界面,所以一并学习. Database 配置 编辑Django的配置文件settings. ...
- Django App(五) load static files
经过前面4篇的努力,已经基本完成了,polls站点的功能,但是所有界面都没有涉及样式,和JavaScript的导入.到目前为止了解到的Django是通过解析Url来完成对客户端的响应的,那么组成站点所 ...
- frist Django app — 一、 创建工程(转载)
转载地址:https://www.cnblogs.com/sunshine-2015/p/5658283.html 缘起 既然python都学了,学习python的时候感觉是相见恨晚,一种新的编程语言 ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第五部分(Page 10)
编写你的第一个 Django app,第五部分(Page 10)转载请注明链接地址 我们继续建设我们的 Web-poll 应用,本节我们会为它创建一些自动测试. 介绍自动测试 什么是自动测试 测试是简 ...
- Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第七部分(Page 12)
编写你的第一个 Django app,第七部分(Page 12)转载请注明链接地址 本节教程承接第六部分(page 11)的教程.我们继续开发 web-poll应用,并专注于自定义django的自动生 ...
- Django 2.0.1 官方文档翻译:编写你的第一个 Django app,第六部分(Page 11)
编写你的第一个 Django app,第六部分(Page 11)转载请注明链接地址 本教程上接前面第五部分的教程.我们构建了一个经过测试的 web-poll应用,现在我们会添加一个样式表和一张图片. ...
随机推荐
- 服务器、应用框架、MVC、MTV
web服务器:负责处理http请求,响应静态文件,常见的有Apache,Nginx以及微软的IIS. 应用服务器:负责处理逻辑的服务器.比如php.python的代码,是不能直接通过nginx这种we ...
- 用js简单实现一下迪克斯特拉算法
今天看书看到了迪克斯特拉算法,大概用js实现一下呢,计算最短路径. 首先,迪克斯特拉算法只适用于有向无环图,且没有负权重,本例关系图如下哦,数字为权重,emmmm,画得稍微有点丑~ //大概用js实现 ...
- 找出n之内的完全数, 并输出其因子
定义: 完全数:所有的真因子(即除了自身以外的约数)的和,恰好等于它本身.例如:第一个完全数是6,它有约数1.2.3.6,除去它本身6外,其余3个数相加,1+2+3=6.第二个完全数是28,它有约数1 ...
- c# Linq&Lambda
0.写这个文章主要记录下常用Lambda的用法,能力有限,文中有问题的地方希望各位大神指出来谢谢!因为平时写代码的时候没有特地去用lambda,全是用一些循环,少量会用到lambda,虽然也能实现要的 ...
- 12 个 JS 技巧
1. 过滤唯一值 ES6 引入了 Set 对象和延展(spread)语法…,我们可以用它们来创建一个只包含唯一值的数组. 复制代码 const array = [1, 1, 2, 3, 5, ...
- 记录一个源码安装mysql5.6的方法
https://www.jb51.net/article/118853.htm 如果之前源码安装过mysql5.6的话,卸载方法如下:rm -rf /var/lib/mysql/rm -rf /usr ...
- git 远程分支回滚
git代码库回滚: 指的是将代码库某分支退回到以前的某个commit id [本地代码库回滚]: git reset --hard commit-id :回滚到commit-id,讲commit-id ...
- 【转】Syncthing – 数据同步利器---自己的网盘,详细安装配置指南,内网使用,发现服务器配置
Syncthing – 数据同步利器---自己的网盘,详细安装配置指南,内网使用,发现服务器配置 原贴:https://www.cnblogs.com/jackadam/p/8568833.html ...
- vue中的组件化
组件化 1.定义全局组件 1.要在父实例中使用某个组件,组件必须在实例值之前定义2.组件其实也是一个Vue实例,因此它在定义时也会接收:data.methond.生命周期函数等3.不同的组件不会与页面 ...
- 移动端css水平垂直居中
水平垂直居中 1.margin 负值调整偏移实现 兼容性: 当前流行的使用方法. <div class="box"> <div class="conte ...