是否有更简单的方法在SqlAlchemy集成测试之间恢复状态?

时间:2016-11-20 09:01:42

标签: python flask sqlalchemy integration-testing flask-sqlalchemy

Flask示例应用程序FlaskyFlaskr在每次测试之间创建,删除和重新播种整个数据库。即使这并没有使测试套件运行缓慢,我想知道是否有办法完成同样的事情而不是如此"破坏性"。我很惊讶,没有一个更软的"回滚任何更改的方法。我已经尝试了一些无用的东西。

对于上下文,我的测试通过Flask test_client使用self.client.post(' / things')之类的东西来调用端点,并在端点内调用session.commit()。

我尝试过制作自己的"承诺"实际上只在测试期间刷新的函数,但是如果我发出两个顺序请求,比如self.client.post(' / things')和self.client.get(' / things') ,新创建的项目不存在于结果集中,因为新请求具有新的请求上下文,其中新的数据库会话(和事务)不知道仅刷新,未提交的更改。这似乎是这种方法不可避免的问题。

我尝试过使用db.session.begin(subtransactions = True)的子交易,但后来遇到了更糟糕的问题。因为我有autoflush = False,所以实际上没有提交或刷新,直到外部事务被提交。因此,任何依赖于同一测试中早期请求修改的数据的请求都将失败。即使使用autoflush = True,顺序请求也会出现早期问题。

我尝试过与子交易相同结果的嵌套交易,显然他们没有做我希望他们做的事情。我看到嵌套事务向DB发出SAVEPOINT命令。我希望这会允许提交,其他会话可见,然后能够在任意时间回滚到该保存点,但这不是他们所做的。它们在交易中使用,并且与先前的方法具有相同的问题。

更新:Apparently there is a way在Connection而不是Session上使用嵌套事务,这可能有效但需要对应用程序进行一些重组才能使用测试代码创建的Connection。我还没试过这个。我最终会解决它,但同时我希望还有另一种方式。有人说这种方法may not work with MySQL是由于" real nested transactions"和保存点,但Postgres documentation也说使用SAVEPOINT而不是尝试嵌套事务。我想我们可以忽视这个警告。我不再看到这两个数据库之间有任何区别,如果它在一个数据库上工作,它可能会在另一个数据库上工作。

避免DB drop_all,create_all和重新播种数据的另一个选项是手动取消测试引入的更改。但是在测试端点时,可以将许多行插入到许多表中,并且可靠地手动撤消这些行将既费力又容易出错。

在尝试了所有这些事情后,我开始看到在测试之间放弃和创建的智慧。但是,我上面尝试过的是否应该有效,但我只是做错了什么?或者还有另一种方法,有人知道我还没有尝试过吗?

更新:Another method I just found on StackOverflow是截断所有表而不是删除和创建它们。这显然快了两倍,但它看起来仍然很苛刻,并不像回滚一样方便(在测试用例之前不会删除放在数据库中的任何样本数据)。

1 个答案:

答案 0 :(得分:1)

对于单元测试,我认为重新生成整个数据库的标准方法是最有意义的,正如您在我的示例和许多其他示例中看到的那样。但我同意,对于大型应用程序,这可能会在您的测试运行期间花费大量时间。

感谢SQLAlchemy,您可以编写许多在生产数据库上运行的通用数据库代码,这些代码可能是MySQL,Postgres等,同时它也可以在sqlite上运行以进行测试。每个应用程序都不可能使用100%通用SQLAlchemy,因为sqlite与其他应用程序有一些重要的区别,但在很多情况下这很有效。

所以只要有可能,我都会为我的测试设置一个sqlite数据库。即使对于大型数据库,使用内存中的sqlite数据库也应该非常快。另一个非常快的替代方法是生成一次表,使用所有emtpy表备份sqlite文件,然后在每次测试之前恢复文件而不是执行create_all()

我没有探讨过使用空表进行数据库初始备份的想法,然后在MySQL或Postgres的测试之间使用基于文件的恢复,但理论上应该也能正常工作,所以我想这是一个解决方案你在你的清单中没有提到。但是,您需要在测试之间停止并重新启动数据库服务。

相关问题