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的更多相关文章

  1. frist Django app — 三、 View

    前面已经说过了Django中model的一些用法,包括orm,以及操作的api,接下来就是搭一些简单的界面学习view——Django中的view.主要介绍以下两个方面: url映射 请求处理 模板文 ...

  2. frist Django app — 四、 完善View

    上一篇已经完成了polls的基本功能,接下来完善剩下的vote功能和并使用generic views改进请求处理view.包含表单的简单运用和前后台参数传递. 目录 vote:完善投票功能 gener ...

  3. frist Django app — 一、 创建工程

    缘起 既然python都学了,学习python的时候感觉是相见恨晚,一种新的编程语言带给我一种新的思考问题的方式,为了巩固学过的东西并进一步学习python,就想学学Django,看看会不会带给我关于 ...

  4. frist Django app— 二、 Model和管理界面

    Django是符合MVC架构的,这里现学习M—Model,而且Django自带了一个管理model(数据库)的界面,所以一并学习. Database 配置 编辑Django的配置文件settings. ...

  5. Django App(五) load static files

    经过前面4篇的努力,已经基本完成了,polls站点的功能,但是所有界面都没有涉及样式,和JavaScript的导入.到目前为止了解到的Django是通过解析Url来完成对客户端的响应的,那么组成站点所 ...

  6. frist Django app — 一、 创建工程(转载)

    转载地址:https://www.cnblogs.com/sunshine-2015/p/5658283.html 缘起 既然python都学了,学习python的时候感觉是相见恨晚,一种新的编程语言 ...

  7. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第五部分(Page 10)

    编写你的第一个 Django app,第五部分(Page 10)转载请注明链接地址 我们继续建设我们的 Web-poll 应用,本节我们会为它创建一些自动测试. 介绍自动测试 什么是自动测试 测试是简 ...

  8. Django 2.0.1 官方文档翻译: 编写你的第一个 Django app,第七部分(Page 12)

    编写你的第一个 Django app,第七部分(Page 12)转载请注明链接地址 本节教程承接第六部分(page 11)的教程.我们继续开发 web-poll应用,并专注于自定义django的自动生 ...

  9. Django 2.0.1 官方文档翻译:编写你的第一个 Django app,第六部分(Page 11)

    编写你的第一个 Django app,第六部分(Page 11)转载请注明链接地址 本教程上接前面第五部分的教程.我们构建了一个经过测试的 web-poll应用,现在我们会添加一个样式表和一张图片. ...

随机推荐

  1. javascript中缺少分号结尾的情况

    首先看一段代码 function* fib (max) { let a = 0 let b = 1 let n = 1 while (n < max) { yield a; [a, b] = [ ...

  2. 真tm郁闷

    昨天这时还是信心满满,今天这时就已经彻底颓了. 感觉这次是最接近的了,4个题目都做出来了,怎么还会fail,那边也不说为什么,到底是哪里不足,尽说些没用的. 前后都当了4次炮灰了,以后也不会再冲动了, ...

  3. 引擎设计跟踪(九.14.3.2) Deferred shading的后续实现和优化

    最近完成了deferred shading和spot light的支持, 并作了一部分优化. 之前forward shading也只支持方向光, 现在也支持了点光源和探照光. 对于forward sh ...

  4. Actifio如何保护和管理Oracle-带外篇

    引言 本文提供CDS带外环境下相关配置,保护和恢复Oracle的所需步骤. 目的是提供Oracle数据库配置前的详细说明,Actifio环境下发现和配置Oracle数据库,执行还原和恢复,以及配置Or ...

  5. c++中函数指针作为int传递

    int f() { ; } typedef int (*method)(); int _tmain(int argc, _TCHAR* argv[]) { int value = (int)& ...

  6. MySQL Transaction--查看未提交事务执行的SQL

    未提交事务 长期未提交事务,指开启事务后,长时间未向MySQL发出SQL执行请求或事务处理(COMMIT/ROLLBACK)请求,在系统表`information_schema`.`INNODB_TR ...

  7. 深度系统 deepin 15.9 关闭桌面

    深度系统 deepin 15.9 关闭桌面 由于特别的原因,关闭深度的桌面. sudo systemctl disable lightdm 如果需要在命令模式进入桌面可以使用以下命令. sudo se ...

  8. Flask之 安装与HelloWorld

    安装Flask 首先我们来安装Flask.最简单的办法就是使用pip. pip install flask 然后打开一个Python文件(app.py),输入下面的内容并运行该文件.然后访问local ...

  9. 弹出的 Dialog 里,包含 Form,如何在关闭 Dialog 时,执行 resetFields(对整个表单进行重置,将所有字段值重置为初始值并移除校验结果)

    做法: before-close 事件中,调用 resetFields 取消按钮事件中,调用 resetFields <el-dialog title="弹出窗口" :vis ...

  10. C语言的抽象与函数指针--思想(转)

    一.何为抽象? 从小到大,我们接触到的抽象,最熟悉的莫过于数学了.为什么这样说呢? 比如说,在小学的时候,老师总是拿了几个苹果来引诱我们:同学们,这里有几个苹果啊?于是我们流着口水一个个地数,一个苹果 ...