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

相關文章
相關標籤/搜索