做者:HelloGitHub-追夢人物html
文中所涉及的示例代碼,已同步更新到 HelloGitHub-Team 倉庫python
評論應用的測試和博客應用測試的套路是同樣的。git
先來創建測試文件的目錄結構。首先在 comments 應用的目錄下創建一個名爲 tests 的 Python 包,而後刪除 comments 應用下 django 自動生成的 tests.py 文件,防止和 tests 包衝突,再根據須要測試的內容,建立相應的 Python 模塊。最終 tests 目錄結構以下:github
comments\
templatetags\
models.py
...
tests\
__init__.py
base.py
test_models.py
test_templatetags.py
test_views.py
複製代碼
其中 base.py 用於存放各個測試用例的公共的數據初始化基類。django
因爲評論必須和文章關聯,所以咱們首先來寫一個數據基類,用於初始化生成文章數據,其它測試類繼承這個數據基類,從而不用在每一個測試類裏都寫一遍建立文章數據的代碼了。編程
數據基類寫在 base.py 模塊裏:bash
comments/tests/base.py
from django.apps import apps
from django.contrib.auth.models import User
from django.test import TestCase
from blog.models import Category, Post
class CommentDataTestCase(TestCase):
def setUp(self):
apps.get_app_config('haystack').signal_processor.teardown()
self.user = User.objects.create_superuser(
username='admin',
email='admin@hellogithub.com',
password='admin'
)
self.cate = Category.objects.create(name='測試')
self.post = Post.objects.create(
title='測試標題',
body='測試內容',
category=self.cate,
author=self.user,
)
複製代碼
要注意建立文章數據時,使用 apps.get_app_config('haystack').signal_processor.teardown()
斷開建立索引的信號。微信
Comment Model 的代碼邏輯比較簡單,測試起來也很簡單:app
comments/tests/test_models.py
from .base import CommentDataTestCase
from ..models import Comment
class CommentModelTestCase(CommentDataTestCase):
def setUp(self):
super().setUp()
self.comment = Comment.objects.create(
name='評論者',
email='a@a.com',
text='評論內容',
post=self.post,
)
def test_str_representation(self):
self.assertEqual(self.comment.__str__(), '評論者: 評論內容')
複製代碼
咱們只有一個發表評論的視圖函數,根據視圖函數的邏輯,須要測試如下幾點:函數
具體代碼以下(省略掉了一些簡單的一看就懂的測試用例):
comments/tests/test_views.py
from django.urls import reverse
from .base import CommentDataTestCase
from ..models import Comment
class CommentViewTestCase(CommentDataTestCase):
def setUp(self):
super().setUp()
self.url = reverse('comments:comment', kwargs={'post_pk': self.post.pk})
# 省略掉了一看就懂的測試用例...
def test_invalid_comment_data(self):
invalid_data = {
'email': 'invalid_email',
}
response = self.client.post(self.url, invalid_data)
self.assertTemplateUsed(response, 'comments/preview.html')
self.assertIn('post', response.context)
self.assertIn('form', response.context)
form = response.context['form']
for field_name, errors in form.errors.items():
for err in errors:
self.assertContains(response, err)
self.assertContains(response, '評論發表失敗!請修改表單中的錯誤後從新提交。')
def test_valid_comment_data(self):
valid_data = {
'name': '評論者',
'email': 'a@a.com',
'text': '評論內容',
}
response = self.client.post(self.url, valid_data, follow=True)
self.assertRedirects(response, self.post.get_absolute_url())
self.assertContains(response, '評論發表成功!')
self.assertEqual(Comment.objects.count(), 1)
comment = Comment.objects.first()
self.assertEqual(comment.name, valid_data['name'])
self.assertEqual(comment.text, valid_data['text'])
複製代碼
首先看到 test_invalid_comment_data
測試用例。這個測試用例中,咱們構造了一個缺失評論內容、評論人名字且郵箱格式不正確的數據,而後將其提交了評論。接着就是對預期結果的斷言。這裏關鍵的一點是,渲染的預覽頁面應該包含提示用戶的表單錯誤。因此咱們從響應的上下文變量中取得表單 form 這個模板變量。接着使用以下代碼獲取表單的錯誤並斷言響應中是否包含了這些錯誤:
for field_name, errors in form.errors.items():
for err in errors:
self.assertContains(response, err)
複製代碼
一旦表單綁定了數據,而且 is_valid
方法被調用,就會有一個 errors
屬性(參考評論視圖函數中表單的處理邏輯)。errors
屬性是一個類字典對象,若是表單數據不包含錯誤,則爲空;若是包含錯誤數據,則其鍵爲包含錯誤數據的字段名稱,值爲該字段錯誤提示構成的列表(一個字段可能包含多個錯誤,因此是一個列表)。例如這裏的 form.errors
,若是將其打印出來(使用 print(repr(form.errors))
,str
方法返回的內容是經渲染的 ul 列表),能夠看到它的內容以下:
{'name': ['這個字段是必填項。'], 'email': ['輸入一個有效的 Email 地址。'], 'text': ['這個字段是必填項。']}
複製代碼
test_valid_comment_data
中,咱們構造合法的評論內容並提交,預期結果是評論提交成功後重定向到被評論文章的詳情頁,因此使用了 assertRedirects
進行斷言。
注意 self.client.post(self.url, valid_data, follow=True)
傳入的 follow=True
參數。因爲評論成功後須要重定向,所以傳入 follow=True
,表示跟蹤重定向,所以返回的響應,是最終重定向以後返回的響應(即被評論文章的詳情頁),若是傳入 False,則不會追蹤重定向,返回的響應就是一個響應碼爲 302 的重定向前響應。
對於重定向響應,使用 assertRedirects
進行斷言,這個斷言方法會對重定向的整個響應的過程進行檢測,默認檢測的是響應碼從 302 變爲 200。
上一篇中介紹過模板標籤的測試方法。基本套路就是代替 django 視圖函數自動渲染模板內容的過程,手工構造一個包含待測試模板標籤的模板,而後手工渲染其內容,斷言渲染後的內容是否包含預期的內容。具體代碼請看源代碼,這裏再也不一一講解,只將涉及的幾個新的表單操做進行一個簡單介紹。
class CommentExtraTestCase(CommentDataTestCase):
# ...省略其它測試用例的代碼
def test_show_comment_form_with_invalid_bound_form(self):
template = Template(
'{% load comments_extras %}'
'{% show_comment_form post form %}'
)
invalid_data = {
'email': 'invalid_email',
}
form = CommentForm(data=invalid_data)
self.assertFalse(form.is_valid())
context = Context(show_comment_form(self.ctx, self.post, form=form))
expected_html = template.render(context)
for field in form:
label = '<label for="{}">{}:</label>'.format(field.id_for_label, field.label)
self.assertInHTML(label, expected_html)
self.assertInHTML(str(field), expected_html)
self.assertInHTML(str(field.errors), expected_html)
複製代碼
看到循環表單 form 的語句:
for field in form:
label = '<label for="{}">{}:</label>'.format(field.id_for_label, field.label)
複製代碼
咱們這裏使用了 field
的兩個屬性,id_for_label
和 id_for_label
,分別是 django 表單自動生成的表單字段 label 的 id 和 label 名。別的就沒什麼好說的了,就是不停地斷言頁面包含預期的 HTML 內容。
至此,咱們完成了對 blog 應用和 comment 應用這兩個核心 app 的測試。如今,咱們想知道的是,到底咱們的測試效果怎麼樣呢?測試充分嗎?測試全面嗎?還有沒有沒有測到的地方呢?
單憑肉眼觀察難以回答上面的問題,接下來咱們就藉助一個工具,從代碼覆蓋率的角度來檢測一下咱們的測試效果究竟如何。
關注公衆號加入交流羣『講解開源項目系列』——讓對開源項目感興趣的人再也不畏懼、讓開源項目的發起者再也不孤單。跟着咱們的文章,你會發現編程的樂趣、使用和發現參與開源項目如此簡單。歡迎聯繫我(微信:xueweihan,備註:講解)加入咱們,讓更多人愛上開源、貢獻開源~