Django'中的模拟函数;s基于类的视图

Django'中的模拟函数;s基于类的视图,django,unit-testing,django-models,python-2.7,python-mock,Django,Unit Testing,Django Models,Python 2.7,Python Mock,我正在使用Django REST框架来开发一个API。出于几个原因,我想使用基于类的视图。然而,我对我的单元测试有点挑剔,我从不允许我的单元测试触及数据库。注意:我总是使用Carl Meyer在Pycon 2012上演示的“技巧”,在那里他模拟了光标包装器 cursor_wrapper = Mock() cursor_wrapper.side_effect = RuntimeError("No touching the database!") @patch('django.db.backend

我正在使用Django REST框架来开发一个API。出于几个原因,我想使用基于类的视图。然而,我对我的单元测试有点挑剔,我从不允许我的单元测试触及数据库。注意:我总是使用Carl Meyer在Pycon 2012上演示的“技巧”,在那里他模拟了光标包装器

cursor_wrapper = Mock()
cursor_wrapper.side_effect = RuntimeError("No touching the database!")

@patch('django.db.backends.util.CursorWrapper', cursor_wrapper)
class TestMyCode(TestCase):
这是如果你对这张幻灯片感兴趣的话

我在其中一个视图中有一个检查数据库中某些内容的方法。为了保持干燥,它由一根柱子和一根柱子共用。但是,我在为我的单元测试模拟它时遇到了问题。这是因为classmethodas_视图创建了一个新的实例和类分派,并返回分派返回的“handler”函数。所以,我似乎无法在基于类的视图中获得共享方法来模拟它

我可以模拟基于类的视图所使用的模型,但是我必须打破我的“枯燥”目标,在POST和PUT中复制代码。我想我可以重构代码并将逻辑转移到模型上。但是,我不确定我想那样做


如何模拟基于类的视图的共享方法以避免实际接触数据库?只是避开他们?

我想你回答了你自己的问题。您用来测试任何web框架的东西也适用于Django,例如控制反转和依赖注入。您可以在Python中保持它的简单性,因此不要被Spring之类的东西吓倒或关闭

为什么不将代码移出基于类的视图?如果出于某种原因,您需要在其他地方使用相同的逻辑,那么您的代码仍然不会干涸。仅仅因为它是Django并不意味着好的编程原则不适用

我建议在新的类/python模块中抽象出一些东西,例如服务(作为一个概念,而不是Django对服务的定义)和用于数据访问的其他逻辑抽象。这样,您就完全独立于Django视图的请求/响应生命周期。Django和Rails开发人员倾向于将每一点逻辑直接放在模型或视图中。这只会导致上帝课程和难以测试的东西

您也可以通过将视图看作一个轻量级抽象来实现这一点,该抽象处理将参数(GET/POST)等编组到代码的其余部分,并调用封装在别处的必要逻辑。依我看,如果您想要可测试的代码,99%的逻辑应该在web上下文之外,除非它对流程绝对重要。这也使得在后台和并行运行更容易

您最终应该得到的是易于测试的普通python模块和类,因为它们对HTTP没有直接依赖关系。如果需要模拟HTTP,只需模拟请求对象即可。幸运的是,python/django的组合使您可以轻松地将这些内容转储并模拟为简单的dicts/kwargs

我意识到使用基于类的视图的一点是,它们很适合用于mixin和强制执行某些约定(返回json、开放图属性等),但是使用直接需要模型(如DetailView)的更“高级”基于类的视图只会使事情变得不必要的复杂。这些视图对于管理员屏幕来说很好,但对于真正的应用程序来说,帮助大于伤害。除非您找到一种很好的无缝方式来集成缓存层等,否则它们很难测试和降低性能。此时,通常只是从视图或TemplateView继承并使用它

特别是关于数据库模拟,创建模拟并检查业务逻辑。那么,输入/输出什么并不重要,只要它符合一组特定的规则和接口。例如,参见类似的内容。您还可以在测试期间简单地创建/销毁临时数据库。一种方法是为dev/staging/production/testing等创建单独的设置模块,并根据环境动态加载它们。这样可以避免在运行单元测试时损坏正在工作的dev数据库。当然,这更像是一种集成测试,但您可能也应该做一些这样的工作。上述解决方案在其他ORM(如Hibernate)中很常见

与前面的相关,您可以在设置中执行类似于下面的代码的操作,以使用内存中的数据库进行单元测试。最后,您仍然需要考虑对实际数据存储类型的集成测试,例如MySQL < /P>
if 'test' in sys.argv:
    DATABASES['default']['ENGINE'] = 'sqlite3'
);tldr

  • 将类视图之外的逻辑放入适当的对象和模块中

  • 不要把自己锁定在试图使各种捆绑的基于类的视图适用于真正的应用程序和每个用例中;你自己滚吧

  • 使用通常良好的TDD原则,如IOC,将所需参数传递给构造函数,松散耦合,避免过多的专有状态要求(特别是HTTP)

  • 通过创建标准的模拟对象(参见#3)和类似服务的接口(参见#1),避免对数据库的依赖