Django tutorial part5

writing_first_django_app_part5

Automated Testing

解决问题一个很好的方法是首先描述这个问题,然后写出代码来实现它。但是很多人习惯先写好代码然后再进行调试找问题。也许在写代码之前先写好一些测试会是更好的选择。

A little bug

前面在我们的Question.was_published_recently()函数中有一个小问题,如果它是一天之内创建的则会返回True,但是如果pub_date是未来的时间也会返回True

通常应用的测试会放到tests.py文件中,现在在polls/tests.py里添加测试:

import datetime

from django.utils import timezone
from django.test import TestCase

from polls.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.assertEqual(future_question.was_published_recently(), False)

这个测试类是继承于TestCase类,它创建了一个将来时间的Question,然后self.assertEqual()函数是测试函数的返回结果,正确应该返回False

运行这个测试:

$ 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.assertEqual(future_question.was_published_recently(), False)
AssertionError: True != False

----------------------------------------------------------------------
Ran 1 test in 0.001s

FAILED (failures=1)
Destroying test database for alias ‘default‘...

首先test polls会在polls应用下查找测试,然后找到TestCase类的子类,就会创建一个特定的数据库来进行测试。测试类中测试的函数会以‘test‘开头,即上面的test_was published_...,最后就是运行assertEqual函数看结果是否与预想的一致,上面的结果是FAILED

修正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‘...

测试异常的情况之后,还要加上一个正确的例子,函数应该返回True:

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.assertEqual(recent_question.was_published_recently(), True)
Test a view

Django提供一个测试Client来模拟一个用户在view测层面与代码进行交互。可以通过tests.py或者shell来进行。

用shell来测试:

# 设置测试环境
>>> from django.test.utils import setup_test_environment
>>> setup_test_environment()

# 建立一个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.core.urlresolvers import reverse
>>> response = client.get(reverse(‘polls:index‘))
>>> response.status_code
200
>>> response.content
‘\n\n\n    <p>No polls are available.</p>\n\n‘
>>> # note - you might get unexpected results if your ``TIME_ZONE``
>>> # in ``settings.py`` is not correct. If you need to change it,
>>> # you will also need to restart your shell session
>>> from polls.models import Question
>>> from django.utils import timezone
>>> # create a Question and save it
>>> q = Question(question_text="Who is your favorite Beatle?", pub_date=timezone.now())
>>> q.save()
>>> # check the response once again
>>> response = client.get(‘/polls/‘)
>>> response.content
‘\n\n\n    <ul>\n    \n        <li><a href="/polls/1/">Who is your favorite Beatle?</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‘]
[<Question: Who is your favorite Beatle?>]

前面我们使用了generic.ListView来创建view,如下:

# polls/views.py

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]

最后返回Question并没有对时间在现在之后的进行筛选,我们作出修改:

from django.utils import timezone

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]

filter(pub_date__lte=timezone.now()返回时间早于或者等于now的Questions

Testing the view

在tests.py中添加对index的测试:

from django.core.urlresolvers import reverse

def create_question(question_text, days):
    """
    Creates a question with the given `question_text` 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‘], [])

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.",
                            status_code=200)

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.>‘]
        )

首先为了方便在测试的时候创建question,先定义函数create_question. 下面几个函数分别对几个情况进行测试,注意函数命名要体现测试的内容。

Test DetailView
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())
# polls/tests.py

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)
        response = self.client.get(reverse(‘polls:detail‘,
                                   args=(future_question.id,)))
        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)
        response = self.client.get(reverse(‘polls:detail‘,
                                   args=(past_question.id,)))
        self.assertContains(response, past_question.question_text,
                            status_code=200)

上面就是一些测试的例子,测试代码可能看起来很多,但其实它们都是非常有用的。When testing, more is better

时间: 2024-11-08 19:23:48

Django tutorial part5的相关文章

Django Tutorial 学习笔记

实际操作了Django入门教程中的范例,对一些细节有了更清晰的掌握.感觉只看文档不动手是不行的,只看文档没法真正掌握其中要素之间的关系,看了很多遍也不行,必须动手做了才能掌握.同时,这次练习在Eclipse+PyDev环境下进行,了解了这个开发环境对Django的支持情况. Django网站提供的入门教程: https://docs.djangoproject.com/en/1.6/intro/tutorial01/ 教程中的数据库结构 Poll {question, pub_date} Cho

Django tutorial part4

writing_first_django_app_part4 Write a simple form 点击每个Question进去detail界面,看到的vote我们希望以选择的形式给用户进行选择,然后将结果以表格的形式POST到服务器. # polls/templates/polls/detail.html <h1>{{ question.question_text }}</h1> {% if error_message %}<p><strong>{{ e

Django tutorial part2

writing_first_django_app_part2 Create super user 下面开始创建管理者帐号: $ python manage.py createsuperuser 输入用户名和密码,邮箱之后创建帐号 运行runserver之后,进入http://127.0.0.1:8000/admin/ 可以登录管理界面 登录之后这时在管理界面还没能看到我们的poll应用,我们需要告诉admin我们的Question对象需要在管理界面显示,修改polls/admin.py: fro

Django tutorial part3

writing_first_django_app_part3 在Django中,网页和其他内容都是通过views来呈现的,每个view由一个简单的python函数来表示,django通过检查url来选择一个view URL pattern: URL的简单通用形式, eg: /newsarchive/<year>/<month>/ python用'URLconfs'来将URL patterns匹配到views 先看一个简单view的例子 # polls/view.py from dj

Django tutorial part1

writing_first_django_app_part1 Creating a project 查看django版本: $ python -c "import django; print(django.get_version())" 创建工程,进入需要放置工程的目录: $ django-admin.py startproject mysite 会创建以下文件: mysite/ manage.py mysite/ __init__.py settings.py urls.py wsg

Django tutorial part6

writing_first_django_app_part6 在django中,网页应用中的一些附加文件,如image, JS, CSS等,称为静态文件"static files" django.contrib.staticfiles: 从各个应用中收集静态文件,放到一个单独的位置,方便使用和管理 Using CSS 静态文件查找路径的时候与template类似,首先在polls目录下创建目录static,在里面再创建一个polls目录,在这个polls目录里面才放置静态文件,现在创建

Pyhon + Django 1.7.2 tutorial + virtualenv简单使用

最近换了工作, 进的team项目中大概是个python + django的组合, python本身的语法以及特性撸过一边之后,这两天按着django官方的文档倒腾了几天, 文档非常详细,本人英语水平也就那样,基本没什么压力,建议像我一样的新手直接去看官方文档,首先内容绝对是更新到了最新的版本,内容组织渐进有序,当然咯,就算按照文档一步一步来,倒腾的过程中总归会碰到些问题,这边博文权当给个记个流水账以后能翻翻或者说有更深的理解了顺便来update下,看看自己的一些思考方式,若果顺便也能给其他人带来

Django 1.6 CBVs

Django 1.6 最佳实践: 如何正确使用 CBVs (Class-based views) Class-based views是Django为解决建站过程中的常见的呈现模式而建立的. 在这节中, 我们着重讲一下CBVs的使用技巧和一般原则. 1. CBVs的使用原则 代码越少越好 永远不要重复代码 View应当只包含呈现逻辑, 不应包括业务逻辑 保持view逻辑清晰简单 不要将CBVs用作403, 404, 500的错误处理程序 保持mixin简单明了 2. 如何使用mixin 在编程中m

【翻译】How To Tango With Django 1.5.4 第五章

5数据模型和数据库 一个模型就是一个描述你数据表的python对象.不用再通过SQL来操作数据库,而是使用python对象来操作数据库. 5.1rango要求 ...一个目录下面有多个下面 ...一个目录有名字,访问量和喜爱量 ...一个页面有题目,URL和一定数量的视图 5.2告诉django你的数据库 在settings.py里面进行配置,添加如下代码: DATABASE_PATH = os.path.join(PROJECT_PATH,'rango.db') DATABASES = { '