如何模拟用于测试的数据库(Java)?

2022-08-31 12:03:10

我用Java编程,我的应用程序正在大量使用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;
}

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

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


答案 1

Java附带Java DB

也就是说,我建议不要使用与生产中使用的数据库不同类型的数据库,除非您通过ORM层。否则,您的SQL可能不像您想象的那么跨平台。

另请查看 DbUnit


答案 2

旧问题的新答案(但事情已经向前发展了一点):

如何模拟用于测试的数据库(Java)?

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

拥有个人测试数据库是不可能的。您必须使用“公共”数据库,每个人都可以访问该数据库

不幸的是,许多公司仍然使用这种模式,但现在我们有了docker,几乎每个数据库都有图像。商业产品有一些限制(如最多几GB的数据),这些限制对于测试并不重要。此外,您还需要在此本地数据库上创建架构和结构

“这些测试肯定不快......” - DB测试往往比通常的测试慢。进行缓慢的测试确实不理想。

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

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

  • 另一个有用的策略是让测试组在默认情况下保持数据库测试关闭(如果它们确实减慢了你的构建速度)。这样,如果有人实际上正在处理db,他需要在cmd行中传递其他标志或使用IDE(testng组和自定义测试选择器非常适合此操作)

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

“需要时间”部分已在上面讨论过。很烦人吗?我看到了两种方式:

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

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

嗯......没有。主要原理是检查您的软件是否生成响应特定输入的所需输出。因此,如果您调用542次,然后返回542,则表示您的软件按指定工作。如果需要,可以在两者之间调用提交/删除缓存。当然,有时你想测试你的实现而不是契约,然后你检查你的dao是否改变了数据库的状态。但是您始终使用sql B测试sql A(插入与选择,序列next_val与返回值等)。是的,你总是会遇到“谁来测试我的测试”的问题,答案是:没有人,所以保持简单!dao.insertdao.count

其他可以帮助您的工具:

  1. 测试容器将帮助您提供真正的数据库。

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

    缺点:

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

    缺点:

    • 仅对小型项目免费
    • 非常年轻的项目
  4. flywayliquibase - db 迁移工具。它们可帮助您轻松地在本地数据库上创建架构和所有结构以进行测试。


推荐