2013-03-27 31 views
6

我正在使用Django REST框架来处理我正在使用的API。出于几个原因,我想使用基于类的视图。但是,我对单元测试有点特别,并且我从不允许单元测试触及数据库。注意:我总是使用Carl Meyer在Pycon 2012上演示的“技巧”,在那里他嘲笑了Cursor包装。Django基于类的视图中的嘲讽函数

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

@patch('django.db.backends.util.CursorWrapper', cursor_wrapper) 
class TestMyCode(TestCase): 

这里是link如果你有兴趣的幻灯片。

我在其中一个视图中检查数据库中的东西的方法。要进行DRY,它将在POST和PUT之间共享。但是,我在单元测试中嘲笑它有问题。这是因为classmethod as_view创建了一个新的实例和类派发,并返回了派发返回值的“处理函数”。所以,我似乎无法在我的基于类的视图中获取共享方法来嘲笑它。

我可以嘲笑基于类的视图使用的模型,但是我必须打破“干”的目标,并在POST和PUT中复制代码。我想我可以重构代码并将逻辑移到模型上。但是,我不积极,我想这样做。

如何嘲笑基于类的视图的共享方法以避免实际触及数据库?避免它们?

回答

3

我想你回答了你自己的问题。你用来测试任何Web框架的东西都适用于Django,比如控制和依赖注入的反转。你可以在Python中保持它非常简单,所以不要被Spring中存在的东西吓倒或关闭。

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

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

您也可以通过将您的视图看作是轻量级抽象来处理其他代码,并调用其他地方封装的必要逻辑来处理像编组参数(GET/POST)等事情。国际海事组织,如果你想要可测试的代码,99%的逻辑应该在Web上下文之外,除非它对于这个过程是绝对关键的。这使得它更容易在背景中并行运行。

你应该最终得到的是普通的python模块和类,它们很容易测试,因为它们没有直接依赖HTTP。如果你需要模拟HTTP,你可以简单地模拟请求对象。你很幸运,因为python/django的组合很容易将这些东西转储出来并作为简单的dicts/kwargs来模拟。

我使用基于类的视图实现的一件事是它们适合与mixin一起使用并强制执行一些约定(返回json,打开图属性等),但使用更“高级”的基于类的视图直接需要模型如DetailView只是使事情不必要地复杂化。这些视图对管理界面来说很不错,但对于真正的应用程序来说,帮助不止于此。除非你找到一个好的,无缝的方式来集成缓存层等,否则他们会使测试和谋杀性能变得很难。此时,通常只是从View或TemplateView继承并完成它。

关于数据库嘲笑具体来说,创建你的嘲笑,并通过你的业务逻辑。那么只要它符合一组特定的规则和接口,就不会影响你输入/输出的内容。例如,请参阅Mixer之类的内容。您也可以在测试期间简单地创建/销毁临时数据库。一种方法是为dev/staging/production/testing等创建单独的设置模块,并根据环境动态加载它们。这样,您可以在运行单元测试时避免损坏工作开发数据库。当然这更多的是进入一种集成测试的形式,但你也应该做一些这样的事情。上述解决方案在其他ORM(如Hibernate)中很常见。

与之前相比,您可以在设置中执行类似以下代码的操作,以使用内存数据库进行单元测试。不过最终,你还是需要考虑集成测试针对实际数据存储类型,例如MySQL的

if 'test' in sys.argv: 
    DATABASES['default']['ENGINE'] = 'sqlite3' 

; tldr

  1. 把你的逻辑课外意见纳入适当的对象和模块。

  2. 不要将自己锁在试图使各种捆绑的基于类的视图适用于真实应用程序和每个用例;推出自己的。

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

    1. 通过创建标准模拟对象(请参阅#3)并通过类似服务的接口(请参阅#1)避免数据库依赖性。