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中单元测试如何插入记录(无删除方法)的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring Boot REST API 中为 db insert 方法编写模拟单元测试?
<Spring Boot / Hibernate> 插入前删除未完成