Spring中单元测试如何插入记录(无删除方法)

Posted

技术标签:

【中文标题】Spring中单元测试如何插入记录(无删除方法)【英文标题】:How unit test insert record in spring (no delete method) 【发布时间】:2018-12-19 00:47:39 【问题描述】:

我有使用 Spring 的 jdbcTemplate 和创建读取更新(无删除)操作的 DAO。

创建方法有 ID 参数,它是表中的唯一键。

除了 mocking DAO,我如何才能真正测试 create 而不会违反约束?

使用随机 ID 有时仍然会失败

我应该覆盖 setAutoCommit 以避免添加记录吗?它仍然认为是有效的单元测试吗?

我必须事先在 SQL 中删除数据库中的记录还是这种类型的测试有 spring 选项?

或者我应该将其视为集成测试而不是单元测试?

编辑

我使用的是 Oracle,我不能使用序列来为 ID 创建值

我们在生产中存在一些数据源(不用于测试)

【问题讨论】:

查看 Enabling and Disabling Transactions 的官方 Spring Framework 参考文档。仅使用@Transactional 就足够了 【参考方案1】:

这真的取决于这种测试的目的是什么,并不是所有的测试在这方面都是“单元测试”。

例如,如果目标是测试封装业务逻辑的“服务”,但从该服务中,有时会调用 DAO,那么最好的方法可能是按照您的建议模拟 DAO。 在这种情况下,DAO 显然不会被这个测试覆盖,但服务会。

如果目的是测试 SQL 语句(我假设 DAO 只包含 SQL 语句 + 可能将它们转换为域对象),那么模拟不是一种选择。

在这种情况下,测试应该包括对某种数据库的调用,但在这种情况下,它不再被称为单元测试(单元测试是运行非常快并且只在内存中运行的东西,没有数据库,没有我/O 等)我将其称为集成测试(正如您也建议的那样),但不同的人可能对此类测试有不同的名称。

在实践中,我们需要这两种测试,因为它们测试不同的东西

那么,如何测试呢?

首先应该做出决定,应该使用哪个数据库,这里有3种方法:

    使用真实数据库运行,在用户之间共享,测试假定它已预先安装 使用内存数据库运行 在测试套件运行时运行数据库的 docker 镜像,之后销毁

虽然关于哪种方法更好的讨论本身就很有趣,但它超出了这个问题 IMO 的范围,每个选择都有其含义。

完成此决定后,您应该从代码中决定如何使用此数据库。

通常弹簧测试使用以下模式:

    在测试前打开一个事务 运行测试(更改数据,甚至更改架构 - 如果需要,添加列、表)。做断言 不管测试结果如何,回滚事务,使数据和测试前一样

因此,如果您对所有测试都采用这种方法,它们将从“空”数据状态开始,这样就不会出现违反约束的情况 这也有效地解决了“删除记录”的问题,因为当事务回滚时,数据无论如何都会被删除。

现在关于删除交易之外的记录。

一个明显的方法是直接从测试中(在 DAO 之外)执行删除的 sql,这样 DAO(生产代码不会被更改)

您可以将 DataSource/JDBCTemplate 直接注入测试中(Spring 测试完全支持这一点)并从那里调用所需的 SQL

【讨论】:

我有 autoCommit=true 并且生产中的数据源很少,您是否建议进行特定测试以在测试中手动注入特定数据源和回滚?为测试维护不同的数据源似乎是开销 spring 测试框架为你配置了一切,如果你把@Transactional 放在测试中,它会回滚事务。关于生产中的多个数据源。您是在生产数据库上运行测试吗? 不,是在测试环境中执行的 我认为您不需要特殊的数据源。如果 autoCommit = true,请保持这样。 Spring 测试框架只是在测试之前打开一个特殊的测试事务,以便在测试中创建的任何事务都只会加入“外部”事务,并且只有在测试之后的回滚才会真正更改数据库(回滚)

以上是关于Spring中单元测试如何插入记录(无删除方法)的主要内容,如果未能解决你的问题,请参考以下文章

单元测试 DAO

如何在 Spring Boot REST API 中为 db insert 方法编写模拟单元测试?

spring-boot 单元测试获取应用程序属性

<Spring Boot / Hibernate> 插入前删除未完成

如何使用 Spring Security 对 Spring 4 DAO 方法进行单元测试?

单插入多读取列表是否安全无锁?