UPDATE 08-07-2012
我可以告诉您我的单元测试实践,这些实践对我自己的目的非常有效,我会给您我的理由:
1.-仅对测试所需但不会改变的信息使用Fixtures,例如,每次测试都需要一个用户,使用basefixture 创建用户.
2.-使用工厂创建你的对象,我个人喜欢FactoryBoy(这来自FactoryGirl,它是一个ruby库).我创建了一个名为Factorys的单独文件.我保存所有这些对象的每个应用程序的py.这样我就不需要测试文件和我需要的所有对象,这使得它更可读,更易于维护.这种方法很酷的一点是,如果您想基于工厂中的某个对象测试其他对象,可以创建一个可以修改的基本对象.而且它不依赖于django,所以当我开始使用mongodb并需要测试它们时,迁移这些对象时,一切都很顺利.现在,在阅读了有关工厂的信息后,人们通常会说:"那我为什么要使用固定装置呢?".因为这些固定装置永远不应该改变,所以工厂里所有额外的东西都是无用的,django对固定装置的支持非常好.
3.-我给外部服务打了Mock个电话,因为这些电话使我的测试非常慢,而且它们依赖于与我的代码对错无关的事情.例如,如果我在测试中发推文,我会测试它是否正确地发推文,复制响应并模拟该对象,以便它每次都返回准确的响应,而不进行实际调用.有时候,当事情出了问题时,进行测试是很好的,而嘲笑对这一点很好.
4.-我使用一台集成服务器(这里我推荐jenkins台),每当我推送到我的暂存服务器时,它都会运行测试,如果失败,它会向我发送一封邮箱.这真是太棒了,因为我经常在上一次的改变中 destruct 了其他东西,忘记了运行测试.它还提供了其他功能,比如覆盖率报告、pylint/jslint/pep8验证,还有很多插件,可以在其中设置不同的统计数据.
关于测试前端的问题,django提供了大约helper functions个基本方法来处理这个问题.
这是我个人使用的,你可以发GET,帖子,登录用户等等,这对我来说已经足够了.我不倾向于使用像Selenium这样的完整前端测试引擎,因为我觉得测试业务层以外的任何其他东西太过分了.我相信有些会有所不同,这总是取决于你在做什么.
除了我的观点之外,django 1.4还为浏览器框架提供了一个非常方便的integration.
我将设置一个示例应用程序,在其中我可以应用这种做法,这样就更容易理解了.让我们创建一个非常基本的博客应用程序:
structure个
blogger/
__init__.py
models.py
fixtures/base.json
factories.py
tests.py
models.py个
from django.db import models
class Blog(models.Model):
user = models.ForeignKey(User)
text = models.TextField()
created_on = models.DateTimeField(default=datetime.now())
fixtures/base.json个
[
{
"pk": 1,
"model": "auth.user",
"fields": {
"username": "fragilistic_test",
"first_name": "demo",
"last_name": "user",
"is_active": true,
"is_superuser": true,
"is_staff": true,
"last_login": "2011-08-16 15:59:56",
"groups": [],
"user_permissions": [],
"password": "IAmCrypted!",
"email": "test@email.com",
"date_joined": "1923-08-16 13:26:03"
}
}
]
factories.py
import factory
from blog.models import User, Blog
class BlogFactory(factory.Factory):
FACTORY_FOR = Blog
user__id = 1
text = "My test text blog of fun"
tests.py个
class BlogTest(TestCase):
fixtures = ['base'] # loads fixture
def setUp(self):
self.blog = BlogFactory()
self.blog2 = BlogFactory(text="Another test based on the last one")
def test_blog_text(self):
self.assertEqual(Blog.objects.filter(user__id=1).count(), 2)
def test_post_blog(self):
# Lets suppose we did some views
self.client.login(username='user', password='IAmCrypted!')
response = self.client.post('/blogs', {'text': "test text", user='1'})
self.assertEqual(response.status, 200)
self.assertEqual(Blog.objects.filter(text='test text').count(), 1)
def test_mocker(self):
# We will mock the datetime so the blog post was created on the date
# we want it to
mocker = Mock()
co = mocker.replace('datetime.datetime')
co.now()
mocker.result(datetime.datetime(2012, 6, 12))
with mocker:
res = Blog.objects.create(user__id=1, text='test')
self.assertEqual(res.created_on, datetime.datetime(2012, 6, 12))
def tearDown(self):
# Django takes care of this but to be strict I'll add it
Blog.objects.all().delete()
请注意,出于示例的目的,我使用了一些特定的技术(顺便说一句,这些技术还没有经过测试).
我必须坚持,这可能不是标准的最佳实践(我怀疑它是否存在),但它对我非常有效.