如何正确使用有状态的Python模块实现测试隔离?
我正在开发的这个项目是一个封装成Python包的业务逻辑软件。 这个想法是,各种脚本或应用程序将导入它,初始化它,然后使用它。
它目前有一个顶层的init()方法来完成初始化并设置各种东西,一个很好的例子是它使用db连接设置SQLAlchemy并存储SA会话以供以后访问。 它被存储在我的项目的一个子包中(即myproj.model.Session,所以其他代码可以在导入模型后得到一个工作的SA会话)。
长话短说,这使得我的软件包成为有状态的软件包。 我正在为这个项目编写单元测试,这种有效的行为带来了一些问题:
我是否应该重构我的包,因为当前的结构不是最佳(可能)实践(tm)? :)
我应该把它留在那里,每次都设置/拆卸整个事物吗? 如果我要实现完全隔离,这意味着在每次测试中完全擦除并重新安装db,是不是过度杀伤?
这个问题实际上是关于整体代码和测试结构的,但是为了我的测试,我使用了nose-1.0。 我知道Isolate插件可能可以帮助我,但我想在测试套件中做出奇怪的事情之前先获得代码。
你有几个选择:
模拟数据库
有几个权衡需要注意。
您的测试将变得更加复杂,因为您必须进行连接的设置,拆卸和模拟。 您可能还想要验证发送的SQL /命令。 它也倾向于创建一种奇怪的紧耦合,这可能会导致您在架构或SQL更改时花费额外的时间维护/更新测试。
这通常是最纯粹的测试隔离,因为它减少了对测试的大量依赖。 它还倾向于加快测试速度,并减少在持续集成环境中自动化测试套件的开销。
用每个测试重新创建数据库
权衡意识。
这可能会使测试非常缓慢,具体取决于实际重新创建数据库需要多长时间。 如果开发数据库服务器是共享资源,则必须额外进行初始投资,以确保每个开发人员在服务器上都有自己的数据库。 取决于测试运行的频率,服务器可能会受到影响。 在持续集成环境中运行测试套件需要额外的开销,因为它至少需要更多的dbs(取决于同时构建多少分支)。
好处与实际运行相同的代码路径和将用于生产的类似资源相关。 这通常有助于早期发现错误,这总是一件非常好的事情。
ORM DB交换
如果您使用SQLAlchemy这样的ORM,那么您可以将底层数据库与可能更快的内存数据库交换。 这样可以减轻以前两个选项的一些负面影响。
它与生产中使用的数据库不完全相同,但ORM应该有助于减轻掩盖错误的风险。 通常,设置内存数据库的时间比文件备份的时间短得多。 它还具有隔离到当前测试运行的好处,因此您不必担心共享资源管理或最终拆卸/清理。
使用相对昂贵的设置(IPython)处理项目时,我已经看到了一种使用get_ipython
函数的方法,该函数设置并返回一个实例,同时用一个函数返回对现有实例的引用。 然后,每个测试都可以调用相同的功能,但它只能完成第一个测试的设置。
这为每次测试节省了很长的设置步骤,但偶尔会造成测试失败或通过的奇怪情况,具体取决于之前运行的测试。 我们有办法处理这个问题 - 很多测试应该做同样的事情,不管状态如何,我们可以尝试在某些测试之前重置对象的状态。 你可能会发现一个类似的权衡为你工作。
模拟是一个简单而强大的工具来实现一些隔离。 有一个来自Pycon2011的视频,展示了如何使用它。 我建议将它与py.test一起使用,这会减少定义测试所需的代码量,并且仍然非常非常强大。
链接地址: http://www.djcxy.com/p/6263.html上一篇: How to correctly achieve test isolation with a stateful Python module?