這里寫目錄標題
- 1、自動化測試簡介
- (1)自動化測試是什么
- (2)為什么要寫測試
- 測試節約你的時間
- 發現錯誤,預防錯誤
- 測試使得代碼更有吸引力
- 2、基礎測試策略
- 3、開始寫第一個測試
- (1)首先得有個bug
- (2)創建一個猜測來暴露這個bug
- (3)運行測試
- (4)修復這個bug
- (5)更全面的測試
- 4、測試視圖
- (1)針對視圖的測試
- (2)Django測試工具之Client
- (3)改善視圖代碼
- (4)測試新視圖
- (5)測試DetailView
- 5、當需要測試的時候,測試用例越多越好
- 6、深入代碼測試
1、自動化測試簡介
(1)自動化測試是什么
測試在不同層次中都存在。有些測試關注很小的細節(函數返回值是否滿足預期),而另一些測試檢查對某個軟件的一系列操作(某一用戶屬兔序列是否造成了預期結果),我們使用shell來測試某一方法的功能,或者運行某個應用并輸入數據來檢查它的行為。
自動化測試是某個系統幫你完成的。當你創建好了一系列測試,每次修改應用代碼后,可以自動檢查出修改后的代碼是否還像預期那樣工作。而不需要花費大量時間手動測試。
(2)為什么要寫測試
對寫復雜項目有用
測試節約你的時間
手動測試浪費時間,要考慮大量數據,但是自動化測試能夠幫助我們在幾秒鐘內完成這件事情
發現錯誤,預防錯誤
測試能夠幫助我們清晰代碼的意圖
測試使得代碼更有吸引力
測試讓其他開發者知道你的代碼通過了測試
2、基礎測試策略
測試驅動——寫代碼之前寫測試
3、開始寫第一個測試
(1)首先得有個bug
現在我們就有一個bug
我們的要求是如果Question是在一天之內發布的,Question.was_published_recently()方法返回True,但是這個方法在Question的pub_date比這個時間還晚的情況下仍然返回True
py manage.py shell
>>> import datetime
>>> from django.utils import timezone
>>> from polls.models import Question
>>> # create a Question instance with pub_date 30 days in the future
>>> future_question = Question(pub_date=timezone.now() + datetime.timedelta(days=30))
>>> # was it published recently?
>>> future_question.was_published_recently()
True
很明顯這是一個錯誤
(2)創建一個猜測來暴露這個bug
我們剛剛手動做的測試就是自動化測試應該做的工作
在 polls/tests.py 中編寫以下代碼
from django.test import TestCase
import datetime
from django.utils import timezone
from .models import Questionclass QuestionModelTests(TestCase):def test_was_published_recetly_with_future_question(self):time=timezone.now()+datetime.timedelta(days=30)future_question=Question(pub_date=time)self.assertIs(future_question.was_published_recently(),False)
解釋一下 assertis 的作用:
future_question.was_published_recently() 調用 was_published_recently 方法,返回一個布爾值。
self.assertIs(future_question.was_published_recently(), False) 斷言 future_question.was_published_recently() 的返回值是 False,即檢查 was_published_recently 方法的結果是否與 False 是同一個對象。
如果 was_published_recently() 返回 False,斷言通過;否則,斷言失敗,測試報告將顯示失敗的信息。
(3)運行測試
py manage.py test polls
這個過程中發生了什么呢
(4)修復這個bug
在 polls/models 中
def was_published_recently(self):now=timezone.now()return now-datetime.timedelta(days=1) <= self.pub_date <= now
再次運行測試
現在通過了測試,可以認為之后都不會出現這個錯誤了
(5)更全面的測試
在 tests.py 中增加
def test_was_published_recently_with_old_question(self):time=timezone.now()-datetime.timedelta(days=1,seconds=1)old_question=Question(pub_date=time)self.assertIs((old_question.was_published_recently(),False))def test_was_published_recently_with_recent_question(self):time=timezone.now()-datetime.timedelta(hours=23,minutes=59,seconds=59)recent_question=Question(pub_time=time)self.assertIs(recent_question.was_published_recently(),True)
4、測試視圖
現在還有一個問題是 我們的polls應用對所有應用“一視同仁”,當pub_date為未來的某一天時,這個問題也照常發布,但是應該在未來的那個時刻發布才對
(1)針對視圖的測試
為了修復這個bug,我們這次先編寫測試,再去寫代碼,也就是“測試驅動”,其實這兩者的順序不是很重要
在開始之前先來看一下需要用到的工具
(2)Django測試工具之Client
dango提供了一個測試使用的Client 來模擬用戶和視圖層代碼的交互,我們能夠在 tests 以及 shell 中使用它
先從shell開始,要做一些在tests 中不是必須的工作:配置測試環境:
(3)改善視圖代碼
我們創建一個未來的投票也會顯示,現在來修復這個問題
def get_queryset(self):return Question.objects.filter(pub_date__lte=timezone.now()).order_by("-pub_date")[:5]
__lte 是 Django 查詢語法中的 “小于或等于”(less than or equal to)的表示方法。
(4)測試新視圖
def create_question(question_text,days):time=timezone.now()+datetime.timedelta(days=days)return Question.objects.create(question_text=question_text,pub_date=time)class QuestionIndexView(TestCase):def test_no_question(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_past_question(self):question=create_question(question_text="Past question",days=-30)response=self.client.get(reverse("polls:index"))self.assertQuerySetEqual(response.context["latest_question_list"],[question],)def test_future_question(self):create_question(question_text="Future question.",days=30)response=self.client.get(reverse("polls:index"))self.assertContains(response,"No polls are available.")self.assertQuerySetEqual(response.context["latest_question_list"],[])def test_future_and_past_question(self):question=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],)def test_two_past_questions(self):question1=create_question(question_text="Past question 1.",days=-30)question2=create_question(question_text="Past question 2.",days=-5)response=self.client.get(reverse("polls:index"))self.assertQuerySetEqual(response.context["latest_question_list"],[question1,question2])
assertIs, assertEqual, assertContains, 和 assertQuerySetEqual 是 Django 測試框架中的一些常用斷言方法,用于在單元測試中進行各種類型的斷言
assertIs:作用:斷言兩個對象是同一個對象(即,兩個對象的引用是相同的)
assertEqual:作用:斷言兩個對象相等(即,兩個對象的值相等)。
測試就是假裝一些管理員的輸入,然后通過用戶端的表現是否符合預期來判斷加入的改變是否破壞了原有的系統狀態
(5)測試DetailView
對于未來的投票,如果用戶輸入了正確的url 還是可以訪問到它們,所以我們在DetailView里增加一些約束內容
polls.view.py
class DetailView(generic.DetailView):model = Questiontemplate_name = "polls/detail.html"def get_queryset(self):return Question.objects.filter(pub_date__lte=timezone.now())
test.py
class QuestionDetailViewTests(TestCase):def test_future_question(self):future_question=create_question(question_text="Future question.",days=5)url=reverse("polls:detail",args=(future_question.id,))response=self.client.get(url)self.assertEqual(response.status_code,404)def test_past_question(self):past_question=create_question(question_text="Past question.",days=-5)url=reverse("polls:detail",args=(past_question.id,))response=self.client.get(url)self.assertContains(response,past_question.question_text)
5、當需要測試的時候,測試用例越多越好
寫完測試就可以忘掉它啦,所以可以讓它肆意增長
對于測試有以下建議
- 對每個模型、視圖建立單獨的 TestClass
- 每個測試方法只測試一個功能
- 給測試起直觀的名字
6、深入代碼測試
Django中的測試