frist Django app — 5、Test

Test——很重要可是没有被重视起来的一个环节,至少是我本身,其实本身以前在作java web的时候就去尝试过怎么作REST接口的测试,一直没有找到一种合适方式,并且由于时间紧没有进一步深究,可是形成的后果每次作了修改以后都测试不充分,引发新的问题,因此此次对于python正好看看Django的单元测试。java

用的是单独的数据库,数据库是干净的(暂未有数据库,test全部操做都是从零开始),不会对正式的数据库形成影响python

Test Model

到如今咱们主要的业务逻辑代码在model和view里面,因此咱们的测试也主要是针对model和view。在Django中咱们的测试代码写在tests.py里面,这里咱们先在models.py的Question类里面添加一个was_published_recently方法:web

    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)

这里先写了三个测试用例,分别测试django

  • 时间大于当前时间的是否会被查到
  • 时间小于当前时间某些天的question是否会被查询到
  • 时间小于当前时间一天内(咱们以前的“最近”的规则设置的就是一天)是否会被查询到

咱们再看看Django为咱们的单元测试提供了怎样的环境。服务器

  • 全部的测试继承自django.test.TestCase,TestCase提供了不少测试方法,好比:assertEqual,assertContains等
  • django会查找全部以test开头的方法(又一个约定大于配置)
  • 使用python manage.py test polls来运行咱们的测试,能够只对某一个app运行测试
  • 每次测试进行的时候,django会建立新的数据库,测试完成以后会删除数据库,这样保证每次测试不会有污染数据

咱们在mysite目录里面运行测试app

python manage.py test polls

能够看到输出ide

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)

再次运行就会发现三个均成功,结果是OKpost

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.>'])
View Code

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

相关文章
相关标签/搜索