如何模拟testing数据库(Java)?

我用Java编程,我的应用程序正在使用数据库。 因此,能够轻松testing我的数据库使用对我来说非常重要。
数据库testing是关于什么的? 对我而言,他们应该提供两个简单的要求:

  1. validationSQL语法。
  2. 更重要的是,根据给定的情况,检查数据是否被正确select/更新/插入。

那么,看来,我需要的只是一个数据库。
但实际上,我不喜欢,因为使用数据库进行testing几乎没有什么困难:

  • “给自己一个testing数据库,可能有多难? – 那么,在我的工作地点,有一个个人testing数据库是不可能的。 你必须使用“公共”数据库,这是每个人都可以访问的。
  • “这些testing肯定不是很快……” – 数据库testing往往比平常testing慢。 有慢testing真的不是理想的。
  • “这个程序应该处理任何情况!” – 尝试和模拟数据库中的每个案例都变得有些恼人,甚至是不可能的。 对于每种情况,应该进行一定数量的插入/更新查询,这是烦人的,需要时间。
  • “等一下,你怎么知道那桌上有542排?” – testing中的主要原则之一是能够以与testing代码不同的方式testingfunction。 在使用数据库时,通常有一种方法可以做某些事情,因此testing与核心代码完全相同。

所以,当涉及到testing的时候,我可能会发现我不喜欢DB(当然,我将不得不在某些时候想到这个),但是在我的testing中,当我发现大多数的bug其余的testing方法)。 但是我在找什么?

我正在寻找一种方法来模拟数据库,模拟数据库,使用文件系统或只是虚拟内存。 我想也许有一个Java工具/包,它允许简单地构造(使用代码接口)每个testing的数据库模拟,模拟的表和行,SQLvalidation,并用一个代码接口来监视其状态(而不是使用SQL )。

你熟悉这种工具吗?


编辑:谢谢你的答案! 虽然我是在问一个工具,但是你也向我提供了一些关于这个问题的提示:)我需要一些时间来检查你的报价,所以我现在不能说你的答案是否令人满意。

无论如何,这里是我正在寻找的更好的视图 – 想象一下名为DBMonitor的类,它的一个特性就是查找表中的行数。 这里是我想用JUnittesting该function的一个想象的代码:

public class TestDBMonitor extends TestCase { @Override public void setUp() throws Exception { MockConnection connection = new MockConnection(); this.tableName = "table1"; MockTable table = new MockTable(tableName); String columnName = "column1"; ColumnType columnType = ColumnType.NUMBER; int columnSize = 50; MockColumn column = new MockColumn(columnName, columnType, columnSize); table.addColumn(column); for (int i = 0; i < 20; i++) { HashMap<MockColumn, Object> fields = new HashMap<MockColumn, Object>(); fields.put(column, i); table.addRow(fields); } this.connection = connection; } @Test public void testGatherStatistics() throws Exception { DBMonitor monitor = new DBMonitor(connection); monitor.gatherStatistics(); assertEquals(((MockConnection) connection).getNumberOfRows(tableName), monitor.getNumberOfRows(tableName)); } String tableName; Connection connection; } 

我希望这段代码足够清晰,以理解我的想法(请原谅我的语法错误,我没有亲爱的Eclipse:P手动打字)。

顺便说一句,我部分使用ORM,而我的原始SQL查询非常简单,不应该因平台而异。

老问题的新答案(但事情已经向前移动了一下):

如何模拟testing数据库(Java)?

你不要模拟它。 你嘲笑你的repositiories,你不testing他们,或者你在testing中使用相同的数据库,你testing你的sqls。 所有的内存数据库都不完全兼容,所以它们不会给你全面的覆盖和可靠性。 并永远不会试图模拟/模拟深的数据库对象,如连接,结果集等,它没有给你任何价值,是一个恶梦开发和维护

有个人testing数据库是不可能的。 你必须使用“公共”数据库,这是每个人都可以访问的

不幸的是,很多公司仍然使用这种模式,但现在我们有docker ,几乎每个分贝都有图像。 商业产品有一些限制(比如高达几gb的数据),这对testing来说并不重要。 你也需要在本地数据库上创build你的模式和结构

“这些testing肯定不是很快……” – 数据库testing往往比平常testing慢。 有慢testing真的不是理想的。

是的,分贝testing速度较慢,但​​并不慢。 我做了一些简单的测量 ,典型的testing花了5-50毫秒。 什么需要时间是应用程序启动。 有很多方法可以加快速度:

  • 第一个DI框架(如spring)提供了一种只运行应用程序的一部分的方法。 如果你用db和non-db相关的逻辑很好的分离你的应用程序,那么在你的testing中你只能启动db部分
  • 每个数据库都有大量的调整选项,使其不太耐用,速度更快。 这是完美的testing。 postgres的例子
  • 你也可以把整个数据库放入tmpfs

  • 另一个有用的策略是有一组testing,并保持数据库testing默认closures(如果他们真的减慢你的构build)。 这样,如果有人实际上在db上工作,他需要在cmd行中传递额外的标志或使用IDE(testng组和自定义testingselect器是完美的)

对于每种情况,应该进行一定数量的插入/更新查询,这是烦人的并且需要时间

上面讨论了“需要时间”的部分。 这是烦人的吗? 我见过两种方式:

  • 为所有testing用例准备一个数据集。 那么你必须保持它和理由。 通常它是从代码中分离出来的。 它有千字节或兆字节。 在一个屏幕上看到很大,理解和推理。 它引入了testing之间的耦合。 因为当你需要更多的testingA行时,testingB中的count(*)失败。 它只会增长,因为即使你删除了一些testing,你也不知道哪一行只被这个testing使用
  • 每个testing都准备好数据。 这样每个testing都是完全独立的,可读性和易于推理。 这是烦人的吗? 伊莫,一点也不! 它可以让您快速编写新的testing,并为您节省大量的工作

你怎么知道表中有542行?“ – testing中的主要原则之一就是能够以不同于testing代码的方式testingfunction

呃…不是真的。 主要原则是检查您的软件是否生成了所需的输出以响应特定的input。 所以如果你调用dao.insert 542次,然后你的dao.count返回542,这意味着你的软件按照规定工作。 如果你愿意,你可以调用之间的提交/放下caching。 当然,有时你想testing你的实现而不是契约,然后你检查你的dao是否改变了db的状态。 但你总是使用sql Btestingsql A(insert vs select,next_val和返回值等)。 是的,你总会遇到“谁来testing我的testing”的问题,答案是:没有人,所以简单点吧!

其他工具,可以帮助你:

  1. testcontainers将帮助您提供真正的数据库。

  2. dbunit – 将帮助您清理testing之间的数据

    缺点:

    • 需要做大量的工作来创build和维护模式和数据。 尤其是当您的项目处于密集的开发阶段时。
    • 这是另一个抽象层,所以如果突然你想使用这个工具不支持的一些数据库function,可能很难testing它
  3. testegration – 意图为您提供完整的,准备使用和可扩展的生命周期(披露:我是一个创造者)。

    缺点:

    • 只有小项目免费
    • 非常年轻的项目
  4. flyway或liquibase – db迁移工具。 他们帮助您轻松地创build架构和本地数据库中的所有结构进行testing。

Java随Java DB一起提供 。

也就是说,我build议不要使用与生产中使用的不同types的DB,除非通过ORM层。 否则,你的SQL可能不像你想象的那样是跨平台的。

也检查出DbUnit

关于如何testing集成点(如通过SQL的数据库连接)有很多观点。 我个人对我有效的一套规则如下:

1)将数据库访问逻辑和function从一般业务逻辑中分离出来,并将其隐藏在接口之后。 原因:为了testing系统中的绝大多数逻辑,最好使用虚拟/存根代替实际的数据库,因为它更简单。 原因2:速度快得多

2)将数据库的testing视为与unit testing主体分离并需要在设置数据库上运行的集成testing原因:testing的速度和质量

3)每个开发者都需要自己独特的数据库。 他们将需要一种自动的方式来根据他们队友的变化更新其结构,并引入数据。 见第4点和第5点。

4)使用像http://www.liquibase.org这样的工具来pipe理数据库结构的升级。; 原因:使您能够灵活地改变现有结构,并在版本中前进

5)使用像http://dbunit.sourceforge.net/这样的工具来pipe理数据。; 为特定的testing用例和基础数据设置场景文件(xml或XLS),并只清除任何一个testing用例所需的内容。 原因:比手动插入和删除数据要好得多原因2:testing人员更容易理解如何调整场景原因3:更快执行此操作

6)您需要functiontesting,其中也包含类似于DBUnit的场景数据,但这是更大的数据集并执行整个系统。 这就完成了以下知识的结合:a)unit testing运行,因此逻辑合理b)对数据库运行和SQL的集成testing是正确的,从而导致“与系统作为一个整体一起工作,底部堆叠“

这个组合对我来说是非常好的,因为它能够实现高质量的testing和产品,并且保持unit testing开发的速度和敏捷性的改变。

为此我使用了Hypersonic 。 基本上,这是一个JAR文件(一个纯Java内存数据库),您可以在自己的JVM中运行,也可以在自己的JVM中运行,并在运行时拥有一个数据库。 然后你停下来,你的数据库就会消失。 我已经使用它 – 迄今为止,作为一个纯粹的内存数据库。 运行unit testing时,通过Ant启动和停止非常简单。

“给自己一个testing数据库,可能有多难? – 那么,在我的工作地点,有一个个人testing数据库是不可能的。 你必须使用“公共”数据库,这是每个人都可以访问的。

听起来就像你在工作中遇到了文化问题,这就给你提供了一个障碍,让你能够充分发挥自己的能力和产品的利益。 你可能想要做些什么。

另一方面,如果数据库模式处于版本控制之下,那么您始终可以拥有一个testing版本,可以从模式创build数据库,使用testing数据填充数据,运行testing,收集结果,然后删除数据库。 它只会在testing期间存在。 如果硬件有问题,它可以是现有安装的新数据库。 这和我们工作的地方很相似。

我们最近切换到JavaDB或德比来实现这一点。 现在,Derby 10.5.1.1实现了一个内存中表示,所以它运行得非常快,不需要到磁盘: Derby In Memory Primer

我们devise我们的应用程序在Oracle,PostgreSQL和Derby上运行,所以我们不会在任何一个平台上走得太远,才会发现一个数据库支持其他function不支持的function。

我认为Acolyte框架可以用于这样的数据库模拟: https : //github.com/cchantep/acolyte 。

它允许运行现有的Java(用于testing)连接你man查询/更新处理:返回适当的结果集,更新计数或警告根据执行情况。

我们现在正在创build一个数据库testing环境。 我们觉得我们必须用真实的数据库pipe理系统来模拟数据 。 模拟数据库pipe理系统的一个问题是,SQL从未真正完全凝聚成为一个标准,所以一个人为的testing环境将不得不忠实地支持我们的生产数据库的方言。 另一个问题是我们大量使用了列值约束,外键约束和唯一约束,而且由于人工工具可能不会实现这些,所以我们的unit testing可以通过,但是当我们的系统testing第一次触及真实限制。 如果testing时间过长,则表示执行错误,我们会调整查询(通常testing数据集与生产相比是微不足道的)。

我们在每台开发人员机器上以及持续集成和testing服务器(我们使用Hudson)上安装了一个真正的DBMS。 我不知道你的工作策略限制是什么,但安装和使用PostgreSQL,MySQL和Oracle XE相当简单。 这些都是免费的开发使用(即使是Oracle XE),所以没有合理的理由来禁止它们的使用。

关键问题是如何确保您的testing始终始终与数据库处于一致的状态? 如果testing全部是只读的,没问题。 如果您可以devise突变testing,以便永远在从未提交的事务中运行,则没有问题。 但是通常你需要担心反转更新。 要做到这一点,您可以将初始状态导出到文件,然后将其导回到testing后(Oracle的exp和imp shell命令执行此操作)。 或者你可以使用检查点/回滚。 但更优雅的方式是使用像dbunit这样的工具,这对我们很好。

这样做的关键优势在于,我们可以在前面捕捉更多的错误,并且在开发人员试图debugging问题时,我们真正的系统testing不会被阻塞。 这意味着我们可以更快,更省力地生成更好的代码。

如果在工作中使用Oracle,则可以使用闪回数据库中的还原点function使数据库返回到testing前的某个时间。 这将清除您个人对数据库的任何更改。

看到:

https://docs.oracle.com/cd/E11882_01/backup.112/e10642/flashdb.htm#BRADV71000

如果您需要一个用于Oracle生产/工作的testing数据库,请从Oracle中查找XE Express版数据库。 这是免费的个人使用,数据库的大小限制在2GB以内。

那么首先,你是否使用任何ORM层进行数据库访问?
如果不是,那么你在想什么是没有用的。当你不确定你正在触发的SQL如何在testing用例中使用别的东西的时候,在生产中你的数据库会如何工作,testing的用法是什么?
如果是的话:那么你可以看看指出的各种选项。

我同意banjollity。 build立独立的开发和testing环境应该是重中之重。 我使用的每个数据库系统都是开源的,或者有一个免费的开发者版本,你可以在你的本地工作站上安装。 这使您可以根据与生产相同的数据库方言进行开发,为开发数据库提供完全的pipe理访问权限,并且比使用远程服务器更快。

尝试使用德比 。 它很容易和便携。 随着hibernate你的应用程序变得灵活。 德比testing,生产你喜欢和信任的任何东西。