测试“不同层"的最佳实践是什么?在姜戈? [英] What are the best practices for testing "different layers" in Django?

查看:38
本文介绍了测试“不同层"的最佳实践是什么?在姜戈?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

不是测试新手,但对在 Django 中测试不同层的建议的混乱感到非常困惑.

有些人建议(他们是对的)避免在模型中使用 Doctests,因为它们不可维护......

其他人说不要使用fixtures,因为它们不如辅助函数灵活,例如..

也有两组人为使用Mock 对象而斗争.第一组相信使用 Mock 并隔离系统的其余部分,而另一组更喜欢停止模拟并开始测试..

我上面提到的主要是关于测试模型.功能测试是另一回事(使用 test.Client() VS webTest VS 等)

是否有任何可维护、可扩展和正确的方法来测试不同的层?

更新

我知道 Carl Meyer 在 2012 年 PyCon 上的演讲..

解决方案

UPDATE 08-07-2012

我可以告诉你我的单元测试实践对我自己的目的非常有效,我会告诉你我的理由:

1.- 仅将 Fixtures 用于测试所需的信息,但不是将要更改,例如,您每次测试都需要一个用户,因此使用 base 固定装置来创建用户.

2.- 使用工厂来创建您的对象,我个人喜欢 FactoryBoy(这来自 FactoryGirl,它是一个 ruby​​ 库).我为保存所有这些对象的每个应用程序创建了一个名为 factory.py 的单独文件.通过这种方式,我将我需要的所有对象都保留在测试文件之外,这使其更具可读性和易于维护.这种方法很酷的一点是,您可以创建一个基础对象,如果您想基于工厂中的某个对象测试其他内容,则可以修改该对象.此外,它不依赖于 django,所以当我开始使用 mongodb 并需要测试它们时迁移这些对象时,一切都很顺利.现在,在阅读了有关工厂的信息后,通常会说那我为什么要使用设备".由于这些装置永远不应该改变工厂的所有额外好东西都是无用的,而 django 开箱即用地支持装置.

3.- 我模拟调用外部服务,因为这些调用使我的测试非常缓慢,而且它们依赖于没有任何东西的东西与我的代码是对还是错有关.例如,如果我在我的测试中发推文,我会测试它是否正确地发推文,复制响应并模拟该对象,以便它每次都返回准确的响应而不进行实际调用.有时也可以在出现问题时进行测试,而模拟对此非常有用.

4.- 我使用运行测试的集成服务器(jenkins 是我在这里的推荐)每次我推送到我的临时服务器时,如果它们失败,它就会向我发送一封电子邮件.这很好,因为我经常发生在上次更改中破坏其他内容而忘记运行测试的情况.它还为您提供其他好处,例如覆盖率报告,pylint/jslint/pep8 验证,并且有很多插件可供您使用设置不同的统计数据.

关于您测试前端的问题,django 附带了一些 helper 函数 以基本方式处理此问题.

这是我个人使用的,你可以触发gets,posts,登录用户等等,对我来说已经足够了.我不倾向于使用像 selenium 这样的完整前端测试引擎,因为我觉得测试除了业务层之外的任何其他东西都是一种矫枉过正.我相信有些人会有所不同,这始终取决于您在做什么.

在我看来,django 1.4 带有一个非常方便的集成 用于浏览器内框架.

我将设置一个示例应用程序,我可以在其中应用这种做法,使其更易于理解.让我们创建一个非常基本的博客应用:

结构

博主/__init__.py模型.py夹具/base.json工厂.py测试文件

models.py

 from django.db 导入模型类博客(模型.模型):用户 = 模型.外键(用户)文本 = 模型.TextField()created_on = models.DateTimeField(default=datetime.now())

fixtures/base.json

<预><代码>[{PK":1,"model": "auth.user",领域":{用户名":fragilistic_test","first_name": "演示","last_name": "用户",is_active":真,is_superuser":真,is_staff":真的,"last_login": "2011-08-16 15:59:56",组":[],用户权限":[],"password": "IAmCrypted!","email": "test@email.com",date_joined":1923-08-16 13:26:03"}}]

factories.py

进口工厂从 blog.models 导入用户,博客类博客工厂(工厂.工厂):FACTORY_FOR = 博客用户__id = 1text = "我的趣味测试文本博客"

tests.py

class BlogTest(TestCase):夹具 = ['base'] # 加载夹具定义设置(自我):self.blog = BlogFactory()self.blog2 = BlogFactory(text="基于上一个的另一个测试")def test_blog_text(self):self.assertEqual(Blog.objects.filter(user__id=1).count(), 2)def test_post_blog(self):# 假设我们做了一些视图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):# 我们将模拟日期时间,因此博客文章是在该日期创建的#我们希望它嘲笑者 = 模拟()co = mocker.replace('datetime.datetime')co.now()mocker.result(datetime.datetime(2012, 6, 12))与嘲笑者:res = Blog.objects.create(user__id=1, text='test')self.assertEqual(res.created_on, datetime.datetime(2012, 6, 12))定义拆卸(自我):# Django 会处理这个问题,但严格来说我会添加它Blog.objects.all().delete()

请注意,为了这个示例,我使用了一些特定的技术(顺便说一下,还没有经过测试).

我必须坚持,这可能不是标准的最佳实践(我怀疑它是否存在),但它对我来说效果很好.

I'm NOT new to testing, but got really confused with the mess of recommendations for testing different layers in Django.

Some recommend (and they are right) to avoid Doctests in the model as they are not maintainable...

Others say don't use fixtures, as they are less flexible than helper functions, for instance..

There are also two groups of people who fight for using Mock objects. The first group believe in using Mock and isolating the rest of the system, while another group prefer to Stop Mocking and start testing..

All I have mentioned above, were mostly in regards to testing models. Functional testing is an another story (using test.Client() VS webTest VS etc. )

Is there ANY maintainable, extandible and proper way for testing different layers??

UPDATE

I am aware of Carl Meyer's talk at PyCon 2012..

解决方案

UPDATE 08-07-2012

I can tell you my practices for unit testing that are working pretty well for my own ends and I'll give you my reasons:

1.- Use Fixtures only for information that is necessary for testing but is not going to change, for example, you need a user for every test you do so use a base fixture to create users.

2.- Use a factory to create your objects, I personally love FactoryBoy (this comes from FactoryGirl which is a ruby library). I create a separate file called factories.py for every app where I save all these objects. This way I keep off the test files all the objects I need which makes it a lot more readable and easy to maintain. The cool thing about this approach is that you create a base object that can be modified if you want to test something else based on some object from the factory. Also it doesn't depend on django so when I migrated these objects when I started using mongodb and needed to test them, everything was smooth. Now after reading about factories it's common to say "Why would I want to use fixtures then". Since these fixtures should never change all the extra goodies from factories are sort of useless and django supports fixtures very well out of the box.

3.- I Mock calls to external services, because these calls make my tests very slow and they depend on things that have nothing to do with my code being right or wrong. for example, if I tweet within my test, I do test it to tweet rightly, copy the response and mock that object so it returns that exact response every time without doing the actual call. Also sometimes is good to test when things go wrong and mocking is great for that.

4.- I use an integration server (jenkins is my recommendation here) which runs the tests every time I push to my staging server and if they fail it sends me an email. This is just great since it happens to me a lot that I break something else in my last change and I forgot to run the tests. It also gives you other goodies like a coverage report, pylint/jslint/pep8 verifications and there exists a lot of plugins where you can set different statistics.

About your question for testing front end, django comes with some helper functions to handle this in a basic way.

This is what I personally use, you can fire gets, posts, login the user, etc. that's enough for me. I don't tend to use a complete front end testing engine like selenium since I feel it's an overkill to test anything else besides the business layer. I am sure some will differ and it always depends on what you are working on.

Besides my opinion, django 1.4 comes with a very handy integration for in-browser frameworks.

I'll set an example app where I can apply this practices so it is more understandable. Let's create a very basic blog app:

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()

Notice I am using some specific technology for the sake of the example (which haven't been tested btw).

I have to insist, this may not be the standard best practice (which I doubt it exists) but it is working pretty well for me.

这篇关于测试“不同层"的最佳实践是什么?在姜戈?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆