H2 postgresql模式似乎对我无效

29

我的应用程序访问Postgres数据库,并且我有许多预定义的查询(例如Rank、Partition、复杂联接等),我针对Postgres发出这些查询。现在,我想使用小型测试数据对这些查询行为进行单元测试。

所以我开始使用H2/JUnit。我发现大多数Postgres查询都像是Rank、Partition、复杂情况下的更新等。因此,我想使用H2 PosgreSQL兼容模式-所有的Postgres查询都会在H2上工作吗?

我按照H2文档中的说法进行了操作:

要使用PostgreSQL模式,请使用数据库URL jdbc:h2:~/test;MODE=PostgreSQL 或SQL语句 SET MODE PostgreSQL。

我使用 SET MODE PostgreSQL 启用了模式,并尝试发出一个包含rank()的查询,它在Postgres中运行正常,但在H2中却不起作用。它给我返回以下异常:

Function "RANK' not found; in SQL statement

我对H2和数据库测试还很陌生。我正在使用H2 JDBC驱动程序来执行Postgres查询,并认为H2 Posgress兼容模式将允许我执行Postgres查询。


7
您可能会发现阅读类似问题的评论很有趣,可以在这里(http://stackoverflow.com/q/15487818/2144390)找到。就我所知,我会支持那些主张如果您的应用程序实际上使用PostgreSQL,则应该对测试也使用PostgreSQL的人。 - Gord Thompson
嗨 @Gord,我希望我能使用Postgres,但是作为单元测试的一部分,我应该使用像H2这样的内存数据库。作为集成测试的一部分,我可以使用Postgres。 - Umesh K
当您使用数据库时,它不是单元测试... - Vortilion
1个回答

52
我想使用H2的PostgreSQL兼容模式,认为所有的PostgreSQL查询都可以在H2上运行,请纠正我如果我错了。
恐怕这不是真的。H2试图模拟PostgreSQL语法并支持一些功能和扩展,但它永远不会完全匹配PostgreSQL的行为,并且不支持所有功能。
你唯一的选择是:
- 在测试中使用PostgreSQL;或者 - 停止使用H2不支持的功能。
我建议在测试中使用Pg。编写一个测试工具来初始化postgres实例并启动测试,然后在测试结束后关闭它相对简单。
根据评论更新:
“单元测试”和“集成测试”之间没有硬性界限。在这种情况下,H2也是一个外部组件。纯粹的单元测试将作为测试工具的一部分使用虚拟响应器进行查询。针对H2的测试与针对PostgreSQL的测试一样是“集成”测试。它在进程内和内存中的事实是方便,但并不具有功能上的重要性。
如果你想进行单元测试,你应该为你的应用程序编写另一个数据库目标,与你的“PostgreSQL”、“SybaseIQ”等目标并列。比如说,称之为“MockDatabase”。它只返回查询的预期结果,而不是实际运行查询,仅存在于测试代码的行为。个人认为这是巨大的时间浪费,但这是单元测试纯粹主义者为避免引入外部依赖项到测试工具中所做的事情。
如果你坚持要对你的DB组件进行单元(而不是集成)测试,但不能/不愿编写模拟接口,那么你必须找到一种使用现有接口的方法。H2将是一个合理的候选人 - 但你必须编写一个新的后端,具有适用于H2的新查询集,你不能只重用你的PostgreSQL后端。正如我们已经确定的,H2不支持你需要在PostgreSQL中使用的所有功能,因此你必须找到使用H2完成相同任务的不同方法。其中一种选择是创建一个简单的H2数据库,其中包含“预期”的结果和简单的查询,返回这些结果,完全忽略真实应用程序的模式。唯一的真正缺点是维护可能会非常麻烦...但这就是单元测试。
个人而言,我只会使用PostgreSQL进行测试。除非我正在测试作为狭窄界面和定义明确的单元的单个类或模块,否则我不关心是否将其称为“单元”或“集成”测试。我会对数据验证类进行单元测试。对于数据库接口代码纯粹主义者来说,单元测试几乎没有意义,我只会做集成测试。
虽然在进程内存数据库中进行测试很方便,但并非必需。您可以编写测试工具包,以使设置代码initdb创建新的PostgreSQL并启动它;然后拆卸代码杀死postmaster并删除datadir。我在this answer中详细介绍了这个问题。
另请参见:
- Running PostgreSQL in memory only 至于:
如果所有查询与预期的结束数据集在Postgress中正常工作,我可以假设它在所有其他数据库中也能正常工作。
如果我正确理解你的意思,那么是的 - 如果你的代码其余部分使用来自PostgreSQL的数据集工作,则通常使用包含相同数据的来自另一个数据库的数据集将产生相同的结果。当然,只要使用简单数据类型而不是特定于数据库的功能。

嗨@Craig,感谢您的回复。我无法停止使用像rank()这样的窗口函数,所以您的意思是我不能在postgres兼容模式下使用H2进行排名。我迷失了,大多数内存数据库都不支持窗口函数。 - Umesh K
如果你的项目真的很重要,你可以在H2中实现窗口函数。这并不容易也不快。个人建议在Pg上进行测试。我不清楚为什么你不能在单元测试中完成这个任务。当然,这很不方便,但比根本没有测试要好。 - Craig Ringer
虽然我同意你的观点,但是有人说我不应该在真实数据库(如Postgres)上进行单元测试,而应该使用内存数据库(如H2)。如果我使用真实数据库(如Postgres),那就是集成测试而不是单元测试。我对数据库测试还很陌生,有些迷茫。 - Umesh K
5
“人们说”?参考资料/链接?听起来像胡说八道。首先,“单元测试”和“集成测试”之间没有明显的分界线。其次,H2也是一个外部组件。纯粹的单元测试需要在测试工具中包含一个虚假的查询响应器。 - Craig Ringer
非常感谢你,Craig。我同意你的观点。我的应用程序与许多数据库进行通信,如Sybase IQ、DB2、Postgres、ParAccel等。我有相同的逻辑查询以不同的形式支持所有这些目标。那么你建议我选择Postgres作为单元测试目标。如果所有查询和预期的结束数据集在Postgres中正常工作,我可以假设它将在所有其他数据库中正常工作,并且它也像我对它们进行手动测试一样正常工作。请指导。你的指导将是一个巨大的帮助。 - Umesh K
@CraigRinger,完全同意您的观点,即H2也是一个外部组件。因此,使用docker来提供测试环境也是一个不错的方法。 - ramsvidor

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接