如何模拟数据库进行测试(Java)?

Eya*_*oth 68 java database testing unit-testing jdbc

我正在用Java编程,我的应用程序正在大量使用DB.因此,能够轻松测试我的数据库使用对我来说非常重要.
DB测试的全部内容是什么?对我来说,他们应该提供两个简单的要求:

  1. 验证SQL语法.
  2. 更重要的是,根据给定的情况检查数据是否被正确选择/更新/插入.

那么,似乎我所需要的只是一个数据库.
但实际上,我不喜欢,因为使用数据库进行测试的困难很少:

  • "只要让自己成为一个测试数据库,它有多难?" - 嗯,在我的工作场所,拥有个人测试数据库是非常不可能的.您必须使用"公共"数据库,每个人都可以访问.
  • "这些测试肯定不会很快......" - DB测试往往比通常的测试慢.进行慢速测试真的不太理想.
  • "这个程序应该处理任何案件!" - 尝试模拟数据库中的每个案例变得有些烦人甚至无法实现.对于每种情况,都应该进行一定量的插入/更新查询,这很烦人并且需要时间.
  • "等一下,你怎么知道那张桌子里有542行?" - 测试的主要原则之一是能够以与测试代码不同的方式测试功能.使用DB时,通常有一种方法可以做某事,因此测试与核心代码完全相同.

所以,你可以弄清楚我在测试时不喜欢数据库(当然,我必须在某些方面达到这个目的,但是在我发现大多数错误使用之后,我宁愿在我的测试后到达那里.其余的测试方法).但我在寻找什么?

我正在寻找一种使用文件系统或虚拟内存来模拟数据库,模拟数据库的方法.我想也许有一个Java工具/包允许简单构建(使用代码接口)每个测试的数据库模拟,模拟表和行,SQL验证,以及用于监视其状态的代码接口(而不是使用SQL) ).

你熟悉这种工具吗?


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

无论如何,这里是我正在寻找的更好的视图 - 想象一个名为DBMonitor的类,它的一个功能是查找表中的行数.这是一个虚构的代码,说明我如何使用JUnit测试该功能:

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;
}
Run Code Online (Sandbox Code Playgroud)

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

顺便说一句,我部分使用ORM,我的原始SQL查询非常简单,不应该从一个平台到另一个平台不同.

yka*_*ich 39

Java自带Java DB.

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

还可以查看DbUnit


pio*_*rek 17

旧问题的新答案(但事情已向前推进了一点):

如何模拟数据库进行测试(Java)?

你不模拟它.你嘲笑你的存储库,你不测试它们,或者你在测试中使用相同的数据库并测试你的sqls.所有内存中的dbs都不完全兼容,因此它们不会为您提供全面的覆盖和可靠性.从来没有尝试模拟/模拟深度数据库对象,如连接,结果集等,它根本没有给你任何价值,是开发和维护的噩梦

拥有个人测试数据库是非常不可能的.您必须使用"公共"数据库,每个人都可以访问

不幸的是,很多公司仍然使用该模型,但现在我们有了docker,几乎每个数据库都有图像.商业产品有一些限制(如高达几gb的数据)对测试不重要.您还需要在此本地数据库上创建架构和结构

"这些测试肯定不会很快......" - DB测试往往比通常的测试慢.进行慢速测试真的不太理想.

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

  • 第一个DI框架(如spring)提供了一种只运行应用程序某些部分的方法.如果您编写的应用程序具有良好的db和非db相关逻辑分离,那么在测试中,您只能启动db部分
  • 每个数据库都有大量的调优选项,使其不那么耐用,速度更快.这非常适合测试.postgres的例子
  • 你也可以把整个db放到tmpfs中

  • 另一个有用的策略是拥有一组测试并默认关闭数据库测试(如果它们确实减慢了你的构建速度).这样,如果有人正在使用db,他需要在cmd行中传递额外的标志或使用IDE(testng组和自定义测试选择器是完美的)

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

上面讨论了"需要时间"部分.这很烦人吗?我见过两种方式:

  • 为您的所有测试用例准备一个数据集.然后你必须维护它并推理它.通常它与代码分开.它有千字节或兆字节.在一个屏幕上看到,理解和推理是很重要的.它引入了测试之间的耦合.因为当您需要更多行进行测试A时,您count(*)的测试B将失败.它只会增长,因为即使你删除了一些测试,你也不知道哪一行只被这一个测试使用
  • 每个测试准备其数据.这样每个测试都是完全独立,可读且易于推理的.这很烦人吗?我没有,完全没有!它可以让您快速编写新测试,并在将来为您节省大量工作

你怎么知道那个表中有542行?" - 测试的主要原则之一是能够以不同于测试代码的方式测试功能

嗯...不是真的.主要原则是检查您的软件是否根据特定输入生成所需的输出.因此,如果您拨打dao.insert542次然后dao.count返回542,则表示您的软件按指定工作.如果需要,可以在两者之间调用commit/drop cache.当然,有时你想测试你的实现而不是合同,然后检查你的dao是否改变了db的状态.但是你总是使用sql B测试sql A(插入vs选择,序列next_val vs返回值等).是的,你总会遇到"谁将测试我的测试"的问题,答案是:没有人,所以保持简单!

其他可以帮助您的工具:

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

  2. dbunit - 将帮助您清理测试之间的数据

    缺点:

    • 创建和维护模式和数据需要做很多工作.特别是当您的项目处于密集开发阶段时.
    • 它是另一个抽象层,所以如果突然想要使用这个工具不支持的某个数据库功能,可能很难测试它
  3. testegration - 旨在为您提供完整,随时可用和可扩展的生命周期(披露:我是创建者).

    缺点:

    • 仅适用于小型项目
    • 非常年轻的项目
  4. 飞路liquibase -数据库迁移工具.它们可以帮助您轻松地创建架构以及本地数据库上的所有结构以进行测试.

  • 我得把它递给你,我不认为有人会重新审视这个问题并且费心去写一个更新的答案.我8年前问过这个问题,从那以后我获得了经验,这使我大多同意你的答案 - 尤其是关于"用相同代码测试功能"的部分. (3认同)

Edd*_*die 10

我为此目的使用了Hypersonic.基本上,它是一个JAR文件(纯Java内存数据库),您可以在自己的JVM或您自己的JVM中运行,并且在它运行时,您有一个数据库.然后你停止它,你的数据库就会消失.到目前为止,我已经将它用作纯粹的内存数据库.在运行单元测试时,通过Ant启动和停止非常简单.


Pau*_*ble 10

关于如何通过SQL测试集成点(如数据库连接)有很多观点.我个人的规则对我有用,如下:

1)将数据库访问逻辑和功能与一般业务逻辑分开,并将其隐藏在接口后面.原因:为了测试系统中的大部分逻辑,最好使用虚拟/存根代替实际数据库,因为它更简单.原因2:速度快得多

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

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

4)使用http://www.liquibase.org等工具管理数据库结构中的升级.原因:使您能够灵活地更改现有结构并在版本中继续前进

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

6)你需要功能测试,它也有类似场景数据的DBUnit,但这是更大的数据集并执行整个系统.这样就完成了结合以下知识的步骤:a)单元测试运行,因此逻辑是正确的b)集成测试到数据库运行并且SQL是正确的,导致"并且整个系统作为一个整体协同工作底部堆栈"

到目前为止,这种组合对于实现高质量的测试和产品以及保持单元测试开发的速度和变化的灵活性非常有用.


Mar*_*ark 5

如果您在工作中使用Oracle,则可以使用“闪回数据库中的还原点”功能使数据库返回到测试之前的某个时间。这将清除您个人对数据库所做的任何更改。

看到:

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

如果您需要用于Oracle生产/工作的测试数据库,则从Oracle查找XE Express Edition数据库。这是免费的供个人使用,数据库的大小限制为小于2GB。


ban*_*ity 5

"只要让自己成为一个测试数据库,它有多难?" - 嗯,在我的工作场所,拥有个人测试数据库是非常不可能的.您必须使用"公共"数据库,每个人都可以访问.

听起来你在工作中遇到了文化问题,这些问题为你能够充分利用自己的能力和产品的利益提供了障碍.您可能想要做些什么.

另一方面,如果您的数据库模式受版本控制,那么您可以始终拥有一个测试版本,该架构从架构创建数据库,使用测试数据填充它,运行测试,收集结果然后删除数据库.它只在测试期间存在.如果硬件有问题,它可以是现有安装的新数据库.这与我工作的地方类似.