我用Java编程,我的应用程序大量使用DB.因此,能够轻松测试我的数据库使用情况对我来说很重要.
数据库测试都是关于什么的?对我来说,它们应该提供两个简单的要求:

  1. 验证SQL语法.
  2. 更重要的是,根据给定情况判断数据是否正确 Select /更新/插入.

那好吧,看来我只需要一个数据库.
但实际上,我不喜欢这样,因为使用DB进行测试几乎没有什么困难:

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

因此,当涉及到测试时,您可以理解我不喜欢DB(当然,在某个时刻我必须谈到这一点,但我宁愿稍后在我的测试中谈到这一点,因为我使用测试方法中的睡觉发现了大多数bug).但我要找的是什么呢?

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

你熟悉这种工具吗?


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

无论如何,下面是我正在寻找的更好的视图-想象一个名为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;
}

我希望这段代码足够清楚,能够理解我的 idea (请原谅语法错误,我没有使用亲爱的Eclipse:p来手动键入).

顺便说一句,我部分使用了ORM,而且我的原始SQL查询非常简单,不同的平台应该没有什么不同.

推荐答案

旧问题的新答案(但事情有了一些进展):

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

你不是在模拟它.你模拟你的存储库,不测试它们,或者在测试中使用相同的数据库,测试你的SQL.所有内存中的dbs都不是完全兼容的,因此它们无法提供完整的覆盖范围和可靠性.永远不要试图模拟/模拟连接、结果集等深db对象.它根本没有任何价值,开发和维护起来是一场噩梦

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

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

"这些测试肯定不快……"-数据库测试往往比通常的测试慢.进行缓慢的测试真的不是很理想.

是的,数据库测试速度较慢,但也没有那么慢.我做了一些简单的measurements次测试,一个典型的测试需要5-50毫秒.需要时间的是应用程序的启动.有很多方法可以加速这一过程:

  • 第一个DI框架(比如spring)提供了一种只运行应用程序的一部分的方法.如果你在编写应用程序时很好地分离了db和非db相关的逻辑,那么在测试中你可以达到start only the db part
  • 每个数据库都有大量的调优选项,这使得它的耐用性较差,速度也快得多.这是测试的最佳 Select .postgres example
  • 您还可以将整个数据库放入tmpfs

  • 另一种有用的策略是设置测试组,并在默认情况下保持关闭数据库测试(如果它们确实会降低构建速度).这样,如果某人确实在使用db,他需要在cmd行中传递额外的标志,或者使用IDE(TestNG组和自定义测试 Select 器非常适合于此)

对于每种情况,都应该进行一定数量的插入/更新查询,这既烦人又耗时

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

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

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

嗯...不是真的.主要原则是判断您的软件是否根据特定的输入生成所需的输出.所以,如果你拨打dao.insert542次,然后你的dao.count返回542,这意味着你的软件按规定工作.如果需要,可以在两者之间调用提交/删除缓存.当然,有时您希望测试实现而不是契约,然后判断dao是否更改了数据库的状态.但您总是使用sql B测试sql A(插入与 Select 、序列下一个值与返回值等).是的,你总是会遇到"谁来测试我的测试"的问题,答案是:没有人,所以保持简单!

其他可能对您有帮助的工具:

  1. testcontainers会帮助您提供 真正的db.

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

    缺点:

    • 创建和维护模式和数据需要进行大量工作.尤其是当您的项目处于密集开发阶段时.
    • 这是另一个抽象层,因此如果您突然想要使用此工具不支持的某些数据库功能,可能很难对其进行测试
  3. testegration-意图为您提供完整、随时可用和可扩展的生命周期(披露:我是创造者).

    缺点:

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

Database相关问答推荐

TYPO3 OOPS,出现错误!编码:202402180809040864ba5c

在使用FT.AGGREGATE聚合数据时,如何在Redis上解析ISO 8601时间?

生产中的超大型 Mnesia 表

如何在 Big Data 中进行模糊搜索

什么是范围锁( range-locks)?

什么技术对处理数百万条记录最有效?

MongoDB:查询具有两个相等字段 $match 和 $eq 的文档

在插入数据库之前而不是在输出时转义 HTML 是不是一个坏主意?

如何从 MySQL 行中修剪前导和尾随引号?

postgresql 在 where 子句中使用 json 子元素

表别名如何影响性能?

在 SQL Server 2005 中将数据库从一个驱动器移动到另一个驱动器的正确方法是什么?

连接字符串中超时

限制一个 sqlite 表的最大行数

PostgreSQL 哈希索引

维护 mgo 会话的最佳实践

在 MYSQL 的子查询中使用 LIMIT 关键字的替代方法

使用 liquibase 更新表中的一行

计算邮政编码...和用户之间的距离.

SqlParameterCollection only accepts non-null SqlParameter type objects, not String objects