為什么我們要寫Unit test? 因為隨着代碼量的增加, 開發人員會忘記, 因此需要Unit test幫助我們保證程序的可靠性. 尤其是我們的程序涉及到醫療信息, 關乎生命安全, 或關乎其他人的資金.
當我們使用manage.py startapp創建新的Django app時, django會為我們創建test.py. 我們需要做的第一步是刪除該文件, 然后建立test_models.py, test_forms.py, test_views.py文件:
myapp/
__init__.py
admin.py
forms.py
models.py
test_forms.py
test_models.py
test_views.py
views.py
如果項目中出現其他代碼文件, 則建立相應的test文件. 這樣做的原因是使測試文件扁平化, 方便我們更容易瀏覽和修改. 注意, 則是文件必須以"test_"開頭, 否則django無法發現這些測試文件.
每個test method應當盡量減少其測試的范圍, 不要嘗試在一個method中測試多個views, models, forms.
當然, 這里也會出現難題, 因為通常一個view會涉及到models, forms, 其他methods和functions. 此時我們就最簡化我們的環境:
# 測試 REST Api
# test_api.py
import json
from django.core.urlresolvers import reverse
from django.test import TestCase
from myapp.models import Article
class ArticleTests(TestCase):
def setUp(self):
Article.objects.get_or_create(title="A title", slug="a-slug")
def test_list(self):
url = reverse("article_object_api")
response = self.client.get(url)
self.assertEquals(response.status_code, 200)
data = json.loads(response.content)
self.assertEquals(len(data), 1)
以上代碼中, 我們使用setUp method, 最簡化了我們需要的model. 以下是一個更為完整的例子, 測試使用django_rest_framework構建的REST API:
# test_api.py
import json
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.utils.http import urlencode
from myapp.models import Article
class ArticleTests(TestCase):
def setUp(self):
Article.objects.get_or_create(title="title1", slug="slug1")
Article.objects.get_or_create(title="title2", slug="slug2")
self.create_read_url = reverse("article_rest_api")
self.read_update_delete_url = reverse("article_rest_api", kwargs={"slug": "slug1"})
def test_list(self):
response = self.client.get(self.create_read_url)
# content中, 兩個title是否都有?
self.assertContains(response, "title1")
self.assertContains(response, "title2")
def test_detail(self):
response = self.client.get(self.read_update_delete_url)
data = json.loads(response.content)
content = {"id": 1, "title": "title1", "slug": "slug1"}
self.assertEquals(data, content)
def test_create(self):
post = {"title": "title3", "slug": "slug3"}
response = self.client.post(self.create_read_url, post)
data = json.loads(response.content)
self.assertEquals(response.status_code, 201)
content = {"id": "3", "title": "title3", "slug": "slug3"}
self.assertEquals(data, content)
self.assertEquals(Article.objects.count(), 3)
def test_delete(self):
response = self.client.delete(self.read_update_delete_url)
self.assertEquals(response.status_code, 204)
self.assertEquals(Article.objects.count(), 1)
django.test.client.RequestFactory可以生成用於view的request. 這樣為我們提供了很大的便利性來定義request, 並能與標准test client隔離. 但是需要注意的是, request factory不支持middleware, 包括session和authentication.
tests應當盡可能的保持簡單. 如果一個tesst中的code過於復雜, 那么我們可能需要為這一test再寫一個test, 可見在這會在debug時造成多大的痛苦.
有時我們需要相似但不同的數據來運行每個test method, 我們可能會嘗試將他們寫到一起, 並通過修改幾個參數來達到test的目的. 但請不要這么做, 最好的方式其實是copy/paste這些代碼, 使其扁平化.
開發過程中, 數據結構的改變導致fixtures難以維護, 之前保存的fixture可能已經不再適用於現在的test. 因此盡量使用django自帶的ORM來取代fixture.
也有其他開發人員喜歡使用以下這些工具:
以下代碼都應當被測試:
對於test class和method, 都應當說明測試目的.
coverage.py為我們提供了哪些部分被測試了, 哪些部分未被測試的詳細信息.
原文鏈接: http://www.weiguda.com/blog/31/
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。