【问题标题】:How to mock users and requests in django如何在 django 中模拟用户和请求
【发布时间】:2011-01-03 10:03:58
【问题描述】:

我有与请求对象或用户对象交互的 django 代码。例如:

foo_model_instance = models.get_or_create_foo_from_user(request.user)

如果您要使用 django python shell 或在单元测试中进行测试,您会在其中传递什么?这里只需一个 User 对象就可以了,但对模拟请求对象的需求也经常出现。

对于外壳或单元测试:

  • 如何模拟用户?
  • 如何模拟请求?

【问题讨论】:

  • “你一直在用那个词,但我不认为它的意思是你认为的......”我认为你的意思是“模型”。
  • @Mike:听起来很有趣,但我认为他说得对。 @pax:打我到妙语:(
  • 我……我必须承认……在我房间的寂静中……深夜……我……是的,是的!我嘲笑用户!他们都是! @perrierism:我们不是在取笑你,我们只是在享受你美妙的词语选择。

标签: python django unit-testing mocking


【解决方案1】:

对于请求,我会使用 Django 中包含的RequestFactory。

from django.test.client import RequestFactory
rf = RequestFactory()
get_request = rf.get('/hello/')
post_request = rf.post('/submit/', {'foo': 'bar'})

对于用户,我会按照@ozan 的建议使用 django.contrib.auth.models.User 并可能使用 factory boy 以提高速度(对于工厂男孩,您可以选择不保存到数据库)

【讨论】:

  • 既然 RequestFactory 可用,这绝对是正确的答案。归功于 Ozan 的回答(实例化真实对象就足够且可取)。我没用过Factory Boy,但如果它接近Rail's Factory Girl的质量,它看起来将是一个很好的选择。
  • 在 django 迁移脚本中为我工作的最佳解决方案。但是当前版本需要设置request.user。此外,要像普通视图一样使用此请求,最好在准备好的请求中包含 request.csrf_processing_done = True(以通过 CSRF 检查)
  • 不能将键值定义为空列表吗?我尝试使用post_request = rf.post('/submit/', {'foo': 'bar','myList':[]}),但使用request.POST 我得到<QueryDict: {u'foo': [u'bar']}>
【解决方案2】:

在此处了解模拟对象
http://en.wikipedia.org/wiki/Mock_object
http://www.mockobjects.com/

并使用这个 python 库来模拟用户
http://python-mock.sourceforge.net/

你也可以自己写一个简单的 User 类,以此为起点

class MockUser(object):
    def __call__(self, *args, **kwargs):
        return self

    def __getattr__(Self, name):
        return self

添加特定案例等

【讨论】:

    【解决方案3】:

    您可以按照 Anurag Uniyal 的建议推出自己的模拟,也可以使用模拟框架。

    对于那些说你可以像在 Django 中一样创建一个普通用户的说法......我建议这违背了单元测试的要点。单元测试不应该涉及数据库,但是通过创建用户,您已经更改了数据库,因此我们要模拟一个。

    【讨论】:

    • 正如丹尼尔所说,测试运行程序会为您创建然后销毁测试数据库,因此您不必担心。
    • 除非您使用数据库,否则它不再是单元测试。它可能仍然是一个完全有效的集成测试,但它不是一个单元测试。
    • 问题是创建和销毁数据库需要时间。每次我做出改变时,我都想最终在眨眼间运行数千次测试。我不想建立一个数据库和一个应用程序实例来运行我的测试。
    • 同意,测试不命中数据库非常有用。即使您使用的是 SQLite 数据库,它仍然比使用模拟的测试慢。
    【解决方案4】:

    您不需要模拟用户,因为您可以在测试中创建一个 - 测试完成后数据库将被销毁。

    要模拟请求,请使用 Simon Willison 的 this snippet。

    【讨论】:

    • 虽然我同意你通常可以为你的测试创建用户 - 有时你不想 - 或者这样做超出了测试的范围。如果我正在测试如果辅助方法返回 False,则该权限被拒绝 - 我将其耦合到数据库是错误的。存在该辅助方法是因为我不想了解数据库表示。
    【解决方案5】:

    你如何模拟用户?

    初始化一个django.contrib.auth.models.User 对象。 User.objects.create_user 让这一切变得简单。

    你如何模拟请求?

    初始化一个django.http.HttpRequest 对象。

    当然,有一些捷径取决于你想做什么。如果您只需要一个具有指向用户的user 属性的对象,只需创建一些东西(任何东西)并赋予它该属性。

    【讨论】:

    • @S.Lott 有时使用真实的东西很好,但随着项目的发展,它最终会变得非常缓慢。可以在几秒钟而不是几分钟内运行模拟测试真是太好了。
    • @TM:也许这通常是正确的。但是 Django 客户端真的很快。您是否有一些替代方案和一些基准来显示节省的时间?
    • @S.Lott 只是体验我自己的项目。使用内置用户的替代方法是使用诸如 pymox 框架之类的东西,或者使用与此处的一些答案中建议的相同的 API 创建自己的模拟对象。使用真实的请求对象不是速度问题。
    • @S.Lott 我并不是说客户端很慢,或者声称模拟比客户端快(我假设“客户端”指的是 djangos 虚拟网络“浏览器”) .我是说它们比数据库访问更快(仅适用于这个问题的User 部分)。如果您不认为模拟比实际访问数据库更快,那么我怀疑您实际上还没有尝试和比较过。在一个非常小的项目中,当我们将代码更改为模拟模型时,我们的团队从需要 1.5 分钟的测试套件变为
    • 我的真实请求有一个.user 属性。 django.http.HttpRequest 的实例没有。我只是在创建 request.user 后设置它。这看起来合理吗?
    猜你喜欢
    • 2013-04-10
    • 1970-01-01
    • 2012-03-11
    • 1970-01-01
    • 2012-07-30
    • 1970-01-01
    • 2013-03-23
    • 2019-08-07
    相关资源
    最近更新 更多