Python 3.x 在类实例的方法内修补变量

Python 3.x 在类实例的方法内修补变量,python-3.x,mocking,python-unittest,django-class-based-views,Python 3.x,Mocking,Python Unittest,Django Class Based Views,我试图了解补丁是如何工作的,我正在使用pytest和Django视图进行测试: views.py from django.contrib.auth.views import LoginView class MyLoginView(LoginView): pass test_view.py from django.test import RequestFactory from .views import MyLoginView rf = RequestFactory() def t

我试图了解补丁是如何工作的,我正在使用pytest和Django视图进行测试:

views.py

from django.contrib.auth.views import LoginView

class MyLoginView(LoginView):
    pass
test_view.py

from django.test import RequestFactory
from .views import MyLoginView

rf = RequestFactory()

def test_login(rf):
    request = rf.get(reverse('myapp:login'))
    response = MyLoginView.as_view()(request)
    assert response.status_code == 200
此操作失败,因为此视图调用数据库以使用以下函数获取当前站点:

失败:不允许访问数据库

如何模拟
获取当前站点()
以避免数据库被命中

一个想法是使用pytest factoryboy的工厂

我设法模拟了
LoginView。获取了上下文数据
,但我无法深入:

from django.test import RequestFactory
from .views import MyLoginView

from django.contrib.sites.models import Site
from pytest_factoryboy import register
from unittest.mock import patch

rf = RequestFactory()


class SiteFactory(factory.Factory):
    class Meta:
        model = Site

register(SiteFactory)


def test_login_social(rf, site_factory):
    request = rf.get(reverse('myapp:login'))
    with patch(
        # 'django.contrib.auth.views.LoginView.get_context_data',  # This is wrong
        'django.contrib.auth.views.get_current_site',  # Solution: Patch where it is imported, this works!
        return_value=site_factory(name='example.com'),
    ):
        response = CommunityLoginView.as_view()(request)
    assert response.status_code == 200

编辑 解决方案是在以下范围内修补调用的方法:

带有补丁('django.contrib.auth.views.get\u current\u site')


由于
上下文数据是


你会怎么做?

这里有两个选项:

  • pytest
    仅允许,如果您显式标记测试函数,我们将访问数据库。如果没有这些信息,
    pytest
    将在没有为测试构建数据库的情况下运行测试。我建议使用和提供的装饰

  • 您已将添加到已安装的应用程序中。此应用程序是可选的,但如果您从单个Django应用程序提供多个不同页面,则此应用程序非常有用。曾经有一段时间,站点框架是强制性的,但由于它是可选的,我很少在我的
    安装的应用程序中包含它。也许你应该把它留给我


  • 编辑:模拟

    当然,模拟也应该工作,因为python中的每个对象都是可模拟的()。请记住,您必须修补模块/函数的位置,因为它绑定到本地作用域

    要找到正确的位置,您可以搜索,查看如何使用以及如何正确修补,或者尝试进入
    PDB
    。我不确定哪种方法有效,但我为您提供了两种选择:

  • pytest--pdb
  • python-mpdb-pytest
    。这将立即打开调试器,您必须
    继续
    一次
    pytest
    现在将运行,直到出现第一个异常,
    PDB
    将自动启动
  • 现在可以使用
    bt
    (回溯)、
    u
    (向上走)、
    l
    (显示源代码)和
    d
    (向下走)查找数据库访问的位置


    编辑2:工厂男孩

    如果您使用的是
    factoryboy
    ,这取决于它是否尝试访问数据库。默认策略是
    .create()
    ,它会写入数据库


    如果您使用
    site\u factory.build()
    ,它应该可以工作,因为这将无法访问您的数据库。

    谢谢!实际上,我并不打算测试Django。这只是一个很难理解的例子,
    patch
    如何以及能做多远。@raratiru我更新了我的答案,以帮助找到模拟给定函数的正确方法。也许在
    获取当前站点()
    旁边还有对数据库的多次访问。感谢您提供pdb技巧和链接。我成功地模拟了
    LoginView。获取了上下文数据
    ,但我无法更深入地找到任何一个。。。现在我明白问题了!不知何故,我错过了您尝试使用
    factoryboy
    创建
    Site
    对象的机会。如果使用
    factoryboy
    创建实例,这取决于它是否尝试访问数据库。默认策略是
    .create()
    ,它会写入数据库。如果您使用
    site\u factory.build()
    ,它应该可以工作,因为它不会访问您的数据库。谢谢,它不会访问数据库,因为我在不访问数据库的情况下修补了
    get\u context\u data
    方法。实际上,我无法修补该方法中定义的变量
    class()
    ->
    def方法(self)
    ->
    variable=调用另一个模块()。