2011-04-15 67 views
7

我正在开发的这个项目是一个封装为Python包的业务逻辑软件。这个想法是,各种脚本或应用程序将导入它,初始化它,然后使用它。如何正确使用有状态的Python模块实现测试隔离?

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

长话短说,这使得我的软件包成为一个有状态的软件包。我在写单元测试项目,这stafeful行为带来了一些问题:

  1. 测试应该孤立的,而是我的包的内部状态,打破这个隔离
  2. 我无法测试的主要init()方法因为它的行为取决于状态
  3. 将来的测试将需要对运行(还没有写)与众所周知的模型状态控制部分(如预填充sqlitein-memory db

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

我应该把它放在那个位置,每次都设置/拆卸整个事物吗?如果我要实现完全隔离,这意味着在每次测试中完全擦除并重新安装db,是不是过度杀伤?

这个问题是真的在整体代码&测试结构,但它为什么值得使用nose-1.0我的测试。我知道Isolate plugin可能可以帮助我,但我希望在测试套件中做出奇怪的事情之前就能获得代码。

回答

2

您有几种选择:

模拟数据库

有一些折衷,以做到心中有数。

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

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

重新创建DB与每个测试

权衡,以做到心中有数。

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

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

ORM DB交换

如果您使用类似的SQLAlchemy的ORM的是,你可以有潜在更快的内存数据库交换基础数据库的可能性。这样可以减轻以前两个选项的一些负面影响。

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

1

使用相对昂贵的设置(IPython)处理项目时,我已经看到一种方法,我们称之为get_ipython函数,该函数设置并返回一个实例,同时用一个返回现有的实例。然后,每个测试都可以调用相同的功能,但它只能完成第一个测试的设置。

这可以为每个测试节省很长的设置步骤,但偶尔会造成测试失败或通过的奇怪情况,具体取决于之前运行的测试。我们有办法处理这个问题 - 很多测试应该做同样的事情,不管状态如何,我们可以尝试在某些测试之前重置对象的状态。你可能会发现一个类似的权衡为你工作。

0

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