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

时间: 2024-07-30 10:08:00

frist Django app — 五、Test的相关文章

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

Django是符合MVC架构的,这里现学习M—Model,而且Django自带了一个管理model(数据库)的界面,所以一并学习. Database 配置 编辑Django的配置文件settings.py进行配置 添加polls app,修改后如下 INSTALLED_APPS = [ 'django.contrib.admin', # 管理界面 'django.contrib.auth', # 认证系统 'django.contrib.contenttypes', # 框架的content t

frist Django app — 三、 View

前面已经说过了Django中model的一些用法,包括orm,以及操作的api,接下来就是搭一些简单的界面学习view——Django中的view.主要介绍以下两个方面: url映射 请求处理 模板文件 url映射和请求处理共同构成了MVC中的C——controller,这里面涉及了url的写法(主要是正则表达式),接收页面传递过来的参数.在Django中可以使用template来进行页面渲染,这样可以直接使用传递过来的数据. url映射 这里紧接着上一篇的工程完善polls项目,polls网站

frist Django app — 一、 创建工程

缘起 既然python都学了,学习python的时候感觉是相见恨晚,一种新的编程语言带给我一种新的思考问题的方式,为了巩固学过的东西并进一步学习python,就想学学Django,看看会不会带给我关于web新的东西.把自己学习过程记录在这里,发现每次写博客都是对学习过的东西的一次整理和提升. 新建一个project startproject python和Django的安装不再详述,我的环境python2.7,Django1.9.7 . django-admin startproject mys

frist Django app — 四、 完善View

上一篇已经完成了polls的基本功能,接下来完善剩下的vote功能和并使用generic views改进请求处理view.包含表单的简单运用和前后台参数传递. 目录 vote:完善投票功能 generic views:改进views.py vote 编辑detail.html,添加投票投票功能 <h1>{{question.question_text}}</h1> {% if error_message %}<p><strong>{{ error_messa

白手起家搭建django app

$django-admin.py startproject web2 $cd web2/ $python manage.py startapp blog $vim web2/settings.py 注意settings.py以下两个部分,首先要配置数据库,其次要在INSTALLED_APP里添加刚才新建的app DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', # Add 'postgresql_psycopg2',

django (三) 第一个django app 创建model

经过上一章,我们已经创建好一个django app了,接下来,我们开始完善它. 首先,我们先了解些基本概念. Django中创建的每个应用程序都是由Python的包组成,遵循某些约定.Django配有一个实用程序,自动生成一个应用程序的基本目录结构,所以您可以专注于编写代码,而不是创建目录. 那么项目和应用程序到底有什么区别呢? 一个app是一个web应用:比如一个博客系统,一个简单的投票系统.而一个项目是一个集合的配置和应用程序特定的Web站点.一个项目可以包含多个应用程序.应用程序可以存在于

Writing your first Django app, part 1

reference: https://docs.djangoproject.com/en/1.8/intro/tutorial01/ Writing your first Django app, part 1 Let’s learn by example. Throughout this tutorial, we’ll walk you through the creation of a basic poll application. It’ll consist of two parts: A

django (二) 第一个django app

第一个django app 我们来做一个投票网站. 他由两部分组成: 一个公共界面让用户去投票 一个管理界面,对投票增删改. 首先,你得创建一个新的项目,只需一行代码,django将自动为你完成这项任务. cd切换到你想存储你项目的路径下如C:\,输入: django-admin.py startproject mysite 这时,你的C:\下将多了一个文件夹mysite,打开后你会看到: mysite/ manage.py mysite/ __init__.py settings.py url

使用Tornado作为Django App的服务器

闲来无事,折腾折腾. 老是听说tonado是个异步web框架和服务器,作为框架倒是了解到了,但是服务器一直不太懂.所以决定了解一下,既然可以做服务器,那就把自己的django app部署到这上边去. 先是*Google*了一下,找到了一篇相关[博客](https://thinkfaster.co/2015/01/run-django-on-tornado/).于是乎,将其改造改造,直接搬过来.原作是要用*websocket*这个东东,而我只是要App部署上去就OK.发现需要的代码是相当精简的.