【问题标题】:How to correctly achieve test isolation with a stateful Python module?如何使用有状态的 Python 模块正确实现测试隔离?
【发布时间】:2011-08-06 05:57:23
【问题描述】:

我正在从事的项目是一个以 Python 包形式包装的业务逻辑软件。这个想法是各种脚本或应用程序将导入它,初始化它,然后使用它。

它目前有一个顶级的 init() 方法来进行初始化并设置各种东西,一个很好的例子是它使用数据库连接设置SQLAlchemy 并存储 SA 会话以供以后访问。它被存储在我的项目的子包中(即 myproj.model.Session,因此其他代码在导入模型后可以获得有效的 SA 会话)。

长话短说,这使我的包成为一个有状态的包。我正在为该项目编写单元测试,这种工作人员的行为带来了一些问题:

  1. 应该隔离测试,但我的包的内部状态打破了这种隔离
  2. 我无法测试 main init() 方法,因为它的行为取决于状态
  3. 未来的测试将需要针对具有众所周知的模型状态(例如预填充的sqlitein-memory db)的(尚未编写的)控制器部分运行

我是否应该以某种方式重构我的包,因为当前结构不是最佳(可能)实践(tm)? :)

我应该把它留在那里,每次都设置/拆除整个东西吗?如果我要实现完全隔离,这意味着在每次测试中完全擦除并重新填充数据库,这不是矫枉过正吗?

这个问题实际上是关于整体代码和测试结构的,但是为了我的测试,我使用nose-1.0 是值得的。我知道Isolate plugin 可能对我有帮助,但我想在测试套件中做奇怪的事情之前得到代码。

【问题讨论】:

    标签: python unit-testing state code-structure


    【解决方案1】:

    你有几个选择:

    模拟数据库

    需要注意一些权衡。

    您的测试将变得更加复杂,因为您必须对连接进行设置、拆卸和模拟。您可能还想验证发送的 SQL/命令。它还往往会产生一种奇怪的紧密耦合,这可能会导致您在架构或 SQL 更改时花费额外的时间来维护/更新测试。

    这通常是最纯粹的测试隔离,因为它减少了来自测试的潜在大依赖。它还倾向于使测试更快,并减少在持续集成环境中自动化测试套件的开销。

    使用每个测试重新创建数据库

    需要注意的权衡。

    这会使您的测试变得非常缓慢,具体取决于重新创建数据库实际需要多少时间。如果开发数据库服务器是共享资源,则必须进行额外的初始投资以确保每个开发人员在服务器上都有自己的数据库。服务器可能会受到影响,具体取决于测试运行的频率。在持续集成环境中运行测试套件会产生额外的开销,因为它至少需要甚至可能更多的数据库(取决于同时构建的分支数量)。

    好处与实际运行将在生产中使用的相同代码路径和类似资源有关。这通常有助于更早地发现错误,这总是一件好事。

    ORM 数据库交换

    如果您使用像 SQLAlchemy 这样的 ORM,您可以将底层数据库与可能更快的内存数据库交换。这可以让您减轻前面两个选项的一些负面影响。

    它与生产环境中使用的数据库并不完全相同,但 ORM 应该有助于降低掩盖错误的风险。通常,设置内存数据库的时间比文件支持的要短得多。它还具有与当前测试运行隔离的好处,因此您不必担心共享资源管理或最终拆卸/清理。

    【讨论】:

      【解决方案2】:

      在一个设置相对昂贵的项目 (IPython) 上工作时,我看到了一种方法,我们调用 get_ipython 函数,该函数设置并返回一个实例,同时用一个返回引用的函数替换自身到现有实例。然后每个测试都可以调用相同的函数,但它只对第一个进行设置。

      这节省了为每个测试执行冗长的设置过程,但有时它会产生奇怪的情况,即测试失败或通过,具体取决于之前运行的测试。我们有办法解决这个问题——无论状态如何,很多测试都应该做同样的事情,我们可以尝试在某些测试之前重置对象的状态。您可能会发现类似的折衷方案适合您。

      【讨论】:

        【解决方案3】:

        Mock 是一个简单而强大的工具来实现一些隔离。 Pycon2011 中有一个很好的video,它展示了如何使用它。我建议将它与 py.test 一起使用,它减少了定义测试所需的代码量,并且仍然非常非常强大。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-09-12
          • 1970-01-01
          • 1970-01-01
          • 2014-09-17
          • 2011-06-23
          • 2019-04-27
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多