在 class 实例的方法中修补变量
Patch a variable inside a method of a class instance
我正在尝试了解补丁的工作原理,并且我正在使用 Django 视图的 pytest 进行测试:
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
失败是因为此视图调用数据库以使用函数 get_current_site()
:
获取当前站点
Failed: Database access not allowed
如何模拟 get_current_site()
以避免数据库命中?
一个想法是使用带有 pytest-factoryboy 的工厂。
我设法模拟了 LoginView.get_context_data
但我无法更深入:
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
编辑
解决方案是在 imported 的范围内修补被调用的方法:
with patch('django.contrib.auth.views.get_current_site')
此处由于context_data
是<class 'django.contrib.sites.models.Site'>
而发生错误
你会怎么做?
这里有两个选择:
pytest
只允许 database access, if you explicitly mark the test function that we will hit the database. Without that information, pytest
will run the test without having constructed a database for tests. I recommend to use pytest-django
and the provided decorator pytest.mark.django_db
.
您已将 Site-Framework 添加到您的 INSTALLED_APPS
。此应用程序是可选的,但如果您从单个 Django 应用程序提供多个不同的页面,则很有用。曾经有一段时间 Site-Framework 是强制性的,但由于它是可选的,所以我很少将其包含在我的 INSTALLED_APPS
中。也许你应该把它留给。
编辑:模拟
当然,模拟也应该有效,因为 python 中的每个对象都是可模拟的(even small numbers). Keep in mind that you have to patch where the module/function is imported 因为它绑定到本地范围。
要找到正确的位置,您可以搜索 Django source code,看看它是如何使用的以及如何正确修补它,或者尝试放入 PDB
。我不确定哪种方式可行,但我为您提供了 2 个选项:
pytest --pdb
python -m pdb pytest
。这将立即打开调试器,您必须 continue
一次。 pytest
现在将 运行 直到第一个异常发生并且 PDB
将自动启动。
您现在可以使用 bt
(回溯)、u
(向上遍历堆栈)、l
(显示源代码)和 d
(向下遍历堆栈)找到数据库访问的位置。
EDIT2:factoryboy
如果您使用 factoryboy
,这取决于 build strategy 它是否尝试访问数据库。默认策略是.create()
,写入数据库。
如果您使用 site_factory.build()
,它应该可以工作,因为这不会访问您的数据库。
我正在尝试了解补丁的工作原理,并且我正在使用 Django 视图的 pytest 进行测试:
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
失败是因为此视图调用数据库以使用函数 get_current_site()
:
Failed: Database access not allowed
如何模拟 get_current_site()
以避免数据库命中?
一个想法是使用带有 pytest-factoryboy 的工厂。
我设法模拟了 LoginView.get_context_data
但我无法更深入:
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
编辑
解决方案是在 imported 的范围内修补被调用的方法:
with patch('django.contrib.auth.views.get_current_site')
此处由于context_data
是<class 'django.contrib.sites.models.Site'>
你会怎么做?
这里有两个选择:
pytest
只允许 database access, if you explicitly mark the test function that we will hit the database. Without that information,pytest
will run the test without having constructed a database for tests. I recommend to usepytest-django
and the provided decoratorpytest.mark.django_db
.您已将 Site-Framework 添加到您的
INSTALLED_APPS
。此应用程序是可选的,但如果您从单个 Django 应用程序提供多个不同的页面,则很有用。曾经有一段时间 Site-Framework 是强制性的,但由于它是可选的,所以我很少将其包含在我的INSTALLED_APPS
中。也许你应该把它留给。
编辑:模拟
当然,模拟也应该有效,因为 python 中的每个对象都是可模拟的(even small numbers). Keep in mind that you have to patch where the module/function is imported 因为它绑定到本地范围。
要找到正确的位置,您可以搜索 Django source code,看看它是如何使用的以及如何正确修补它,或者尝试放入 PDB
。我不确定哪种方式可行,但我为您提供了 2 个选项:
pytest --pdb
python -m pdb pytest
。这将立即打开调试器,您必须continue
一次。pytest
现在将 运行 直到第一个异常发生并且PDB
将自动启动。
您现在可以使用 bt
(回溯)、u
(向上遍历堆栈)、l
(显示源代码)和 d
(向下遍历堆栈)找到数据库访问的位置。
EDIT2:factoryboy
如果您使用 factoryboy
,这取决于 build strategy 它是否尝试访问数据库。默认策略是.create()
,写入数据库。
如果您使用 site_factory.build()
,它应该可以工作,因为这不会访问您的数据库。