在类实例的方法内部修补变量

时间:2018-10-23 20:29:05

标签: python-3.x mocking python-unittest django-class-based-views

我试图了解修补程序的工作原理,并使用pytest测试Django视图:

views.py

#include <type_traits>
#include <iostream>

class TypeA {};
class TypeB {};
class TypeAB: public TypeA, public TypeB {};

template <typename T>
struct Container
 {
   private:
      T* ptr;

   public:
      Container(): ptr{new T{}} {}

      template <typename U>
      auto get_Type()
         -> std::enable_if_t<true == std::is_base_of<U, T>::value, U*>
       { return ptr; }

      template <typename U>
      auto get_Type()
         -> std::enable_if_t<false == std::is_base_of<U, T>::value, U*>
       { return nullptr; }
};

int main ()
 {
   Container<TypeA> typea;
   Container<TypeB> typeb;
   Container<TypeAB> typeab;

   std::cout << typea.get_Type<TypeA>() << std::endl; //valid pointer
   std::cout << typea.get_Type<TypeB>() << std::endl; //nullptr

   std::cout << typeb.get_Type<TypeA>() << std::endl; //nullptr
   std::cout << typeb.get_Type<TypeB>() << std::endl; //valid pointer

   std::cout << typeab.get_Type<TypeA>() << std::endl; //valid pointer
   std::cout << typeab.get_Type<TypeB>() << std::endl; //valid pointer
 }

test_view.py

from django.contrib.auth.views import LoginView

class MyLoginView(LoginView):
    pass

此操作失败,因为此视图调用数据库以使用功能get_current_site()获取当前站点:

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

如何模拟Failed: Database access not allowed以避免数据库命中?

一个想法是与pytest-factoryboy一起使用工厂。

我设法嘲笑get_current_site(),但我无法深入:

LoginView.get_context_data

编辑

解决方案是在名为imported的范围内修补方法beeing:

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


由于with patch('django.contrib.auth.views.get_current_site')context_data

,在此发生错误

你会怎么做?

1 个答案:

答案 0 :(得分:1)

您在这里有两个选择:

  1. pytest仅允许database access,如果您明确标记了我们将要访问数据库的测试函数。如果没有这些信息,pytest将在不构建测试数据库的情况下运行测试。我建议使用pytest-django和提供的装饰器pytest.mark.django_db

  2. 您已将Site-Framework添加到INSTALLED_APPS中。该应用程序是可选的,但如果您通过单个Django应用程序提供多个不同的页面,则该实用程序很有用。曾经有一段时间,网站框架是强制性的,但是由于它是可选的,因此我很少在我的INSTALLED_APPS中加入它。也许您应该将其保留。


编辑:模拟

当然,由于python中的每个对象都是可模拟的(even small numbers),因此模拟也应该起作用。请记住,由于模块/功能is imported已绑定到本地范围,因此必须对其进行修补。

要找到正确的位置,您可以搜索Django source code,查看其用法和正确打补丁的方法,或尝试放入PDB。我不确定哪种方法可行,但是我为您提供了两种选择:

  1. pytest --pdb
  2. python -m pdb pytest。这将立即打开调试器,您必须continue一次。 pytest现在将运行直到出现第一个异常,并且PDB将自动开始。

您现在可以使用bt(回溯),u(步行堆栈),l(显示源代码)和d(步行堆栈)来查找数据库访问的位置。


EDIT2:工厂男孩

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

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