为 Django 应用程序编写良好的测试
我一生中从未编写过任何测试,但我想开始为我的 Django 项目编写测试。我读过一些关于测试的文章,并决定尝试为一个非常简单的 Django 应用程序或开始编写一些测试。
该应用程序有两个视图(列表视图和详细信息视图)和一个包含四个字段的模型:
class News(models.Model):
title = models.CharField(max_length=250)
content = models.TextField()
pub_date = models.DateTimeField(default=datetime.datetime.now)
slug = models.SlugField(unique=True)
我想向您展示我的tests.py 文件并询问:
这有意义吗?
我是否测试了正确的东西?
是否有我没有遵循的最佳实践,你可以指出我吗?
我的tests.py(它包含 11 个测试):
# -*- coding: utf-8 -*-
from django.test import TestCase
from django.test.client import Client
from django.core.urlresolvers import reverse
import datetime
from someproject.myapp.models import News
class viewTest(TestCase):
def setUp(self):
self.test_title = u'Test title: bąrekść'
self.test_content = u'This is a content 156'
self.test_slug = u'test-title-bareksc'
self.test_pub_date = datetime.datetime.today()
self.test_item = News.objects.create(
title=self.test_title,
content=self.test_content,
slug=self.test_slug,
pub_date=self.test_pub_date,
)
client = Client()
self.response_detail = client.get(self.test_item.get_absolute_url())
self.response_index = client.get(reverse('the-list-view'))
def test_detail_status_code(self):
"""
HTTP status code for the detail view
"""
self.failUnlessEqual(self.response_detail.status_code, 200)
def test_list_status_code(self):
"""
HTTP status code for the list view
"""
self.failUnlessEqual(self.response_index.status_code, 200)
def test_list_numer_of_items(self):
self.failUnlessEqual(len(self.response_index.context['object_list']), 1)
def test_detail_title(self):
self.failUnlessEqual(self.response_detail.context['object'].title, self.test_title)
def test_list_title(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].title, self.test_title)
def test_detail_content(self):
self.failUnlessEqual(self.response_detail.context['object'].content, self.test_content)
def test_list_content(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].content, self.test_content)
def test_detail_slug(self):
self.failUnlessEqual(self.response_detail.context['object'].slug, self.test_slug)
def test_list_slug(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].slug, self.test_slug)
def test_detail_template(self):
self.assertContains(self.response_detail, self.test_title)
self.assertContains(self.response_detail, self.test_content)
def test_list_template(self):
self.assertContains(self.response_index, self.test_title)
I've never written any tests in my life, but I'd like to start writing tests for my Django projects. I've read some articles about tests and decided to try to write some tests for an extremely simple Django app or a start.
The app has two views (a list view, and a detail view) and a model with four fields:
class News(models.Model):
title = models.CharField(max_length=250)
content = models.TextField()
pub_date = models.DateTimeField(default=datetime.datetime.now)
slug = models.SlugField(unique=True)
I would like to show you my tests.py file and ask:
Does it make sense?
Am I even testing for the right things?
Are there best practices I'm not following, and you could point me to?
my tests.py (it contains 11 tests):
# -*- coding: utf-8 -*-
from django.test import TestCase
from django.test.client import Client
from django.core.urlresolvers import reverse
import datetime
from someproject.myapp.models import News
class viewTest(TestCase):
def setUp(self):
self.test_title = u'Test title: bąrekść'
self.test_content = u'This is a content 156'
self.test_slug = u'test-title-bareksc'
self.test_pub_date = datetime.datetime.today()
self.test_item = News.objects.create(
title=self.test_title,
content=self.test_content,
slug=self.test_slug,
pub_date=self.test_pub_date,
)
client = Client()
self.response_detail = client.get(self.test_item.get_absolute_url())
self.response_index = client.get(reverse('the-list-view'))
def test_detail_status_code(self):
"""
HTTP status code for the detail view
"""
self.failUnlessEqual(self.response_detail.status_code, 200)
def test_list_status_code(self):
"""
HTTP status code for the list view
"""
self.failUnlessEqual(self.response_index.status_code, 200)
def test_list_numer_of_items(self):
self.failUnlessEqual(len(self.response_index.context['object_list']), 1)
def test_detail_title(self):
self.failUnlessEqual(self.response_detail.context['object'].title, self.test_title)
def test_list_title(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].title, self.test_title)
def test_detail_content(self):
self.failUnlessEqual(self.response_detail.context['object'].content, self.test_content)
def test_list_content(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].content, self.test_content)
def test_detail_slug(self):
self.failUnlessEqual(self.response_detail.context['object'].slug, self.test_slug)
def test_list_slug(self):
self.failUnlessEqual(self.response_index.context['object_list'][0].slug, self.test_slug)
def test_detail_template(self):
self.assertContains(self.response_detail, self.test_title)
self.assertContains(self.response_detail, self.test_content)
def test_list_template(self):
self.assertContains(self.response_index, self.test_title)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我的测试并不完美,但有一些想法:
这意味着您不必测试框架提供的函数、类等。
也就是说,快速检查您的测试函数:
test_detail_status_code
和test_list_status_code
:好的,检查一下你的路由配置是否正确。当您提供自己的
get_absolute_url()
实现时,更重要的是。test_list_numer_of_items
:好的,如果视图应该返回一定数量的项目。如果数字不重要(即任意),则不需要。
test_detail_template
和test_list_template
:可以检查模板变量是否设置正确。
所有其他功能:没有必要。
您在这里基本上测试的是 ORM 是否正常工作、列表是否按预期工作以及对象属性是否可以访问(或不可访问)。只要您不更改,例如模型的
save()
方法和/或提供您的自定义逻辑,我就不会测试这。您应该相信框架开发人员这可以正常工作。您只需要测试您已经(重写)编写的内容。
模型类可能是一个特例。正如我所说,如果您提供自定义逻辑,您基本上必须测试它们。但您还应该根据您的要求测试它们。例如,字段可能不允许为 null(或者它必须是某种数据类型,例如整数)。因此,如果该字段中包含
null
值,您应该测试存储对象是否失败。这不会测试 ORM 是否正确遵循您的规范,而是测试规范是否仍然满足您的要求。您可能更改了模型并更改了某些设置(偶然或因为您忘记了要求)。
但您不必测试诸如
save()
之类的方法或是否可以访问属性。当然,当您使用有缺陷的第三方代码时……事情可能会有所不同。但由于 Django 使用测试框架本身来验证一切是否正常工作,我认为它正在工作。
总结一下:
根据您的要求进行测试,测试您自己的代码。
这只是我的观点。也许其他人有更好的建议。
I am not perfect in testing but a few thoughts:
This implies that you don't have to test functions, classes, etc. which the framework provides.
That said, a quick check of your test functions:
test_detail_status_code
andtest_list_status_code
:Ok to check whether you have configured the routing properly or not. Even more important when you provide your own implementation of
get_absolute_url()
.test_list_numer_of_items
:Ok if a certain number of items should be returned by the view. Not necessary if the number is not important (i.e. arbitrary).
test_detail_template
andtest_list_template
:Ok to check whether template variables are correctly set.
All the other functions: Not necessary.
What your are basically testing here is whether the ORM worked properly, whether lists work as expected and whether object properties can be accessed (or not). As long as you don't change e.g. the
save()
method of a model and/or provide your custom logic, I would not test this. You should trust the framework developers that this works properly.You only should have to test what you have (over)written.
The model classes are maybe a special case. You basically have to test them, as I said, if you provide custom logic. But you should also test them against your requirements. E.g. it could be that a field is not allowed to be
null
(or that it has to be a certain datatype, like integer). So you should test that storing an object fails, if it has anull
value in this field.This does not test the ORM for correctly following your specification but test that the specification still fulfills your requirements. It might be that you change the model and change some settings (by chance or because you forgot about the requirements).
But you don't have to test e.g. methods like
save()
or wether you can access a property.Of course when you use buggy third party code... well things can be different. But as Django uses the test framework itself to verify that everything is working, I would assume it is working.
To sum up:
Test against your requirements, test your own code.
This is only my point of view. Maybe others have better proposals.
将您的测试分成两种完全不同的类型。
模型测试。将它们与模型一起放入
models.py
文件中。这些测试将练习模型类中的方法。您可以执行简单的 CRUD(创建、检索、更新、删除)来简单地证明您的模型有效。不要测试每个属性。如果您好奇,请测试字段默认值和save()
规则。对于您的示例,创建一个
TestNews
类,用于创建、获取、更新和删除News
项。请务必测试默认日期结果。这堂课应该简短而切题。如果您的应用程序需要,您可以测试各种过滤器处理。您的单元测试代码可以(并且应该)提供过滤News
的“正确”方法的示例。用户界面测试。将它们放在单独的
tests.py
文件中。这些测试将测试视图函数和模板。用您正在创建的“条件”命名测试用例。 “测试未登录”。 “测试登录”。 “测试NoValidThis”。 “测试不允许这样做”。您的
setUp
将执行登录以及建立所需条件所需的任何其他步骤。用操作和结果命名每个测试方法。 “test_get_noquery_should_list”、“test_post_should_validate_with_errors”、“test_get_query_should_detail”。
Break your tests into two completely separate kinds.
Model tests. Put these in your
models.py
file with your model. These tests will exercise the methods in your model classes. You can do simple CRUD (Create, Retrieve, Update, Delete) to simply prove that your model works. Don't test every attribute. Do test field defaults andsave()
rules if you're curious.For your example, create a
TestNews
class that creates, gets, updates and deletes aNews
item. Be sure to test the default date results. This class should be short and to the point. You can, if your application requires it, test various kinds of filter processing. Your unit test code can (and should) provide examples of the "right" way to filterNews
.UI Tests. Put these in a separate
tests.py
file. These tests will test the view functions and templates.Name the TestCase with the "condition" you're creating. "TestNotLoggedIn". "TestLoggedIn". "TestNoValidThis". "TestNotAllowedToDoThat". Your
setUp
will do the login and any other steps required to establish the required condition.Name each test method with the action and result. "test_get_noquery_should_list", "test_post_should_validate_with_errors", "test_get_query_should_detail".