我正在使用JUnit 5和H2数据库进行测试。从逻辑上讲,JUnit是单元测试,所以测试方法不应该相互依赖。另外,JUnit 以随机顺序运行测试方法。
然后出现了一个悖论。如果通过某种方法从数据库中删除了一条记录,而在另一种方法中将访问该记录,该记录已被删除,该怎么办?
我举个例子。一种方法检查是否已通过 id 从数据库表中删除记录。另一种方法是通过 id 读取该条目。
如果你设置了测试执行的顺序,那么这不再是单元测试,因为。一种测试方法依赖于另一种测试方法。
更新 1
感谢用户@andreymal 的评论
您可以在测试类上声明注释:
import org.springframework.test.annotation.DirtiesContext;
@Sql(scripts = "classpath:db/schema.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "classpath:db/data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
这里没有悖论。问题是您的测试不是孤立的(并且应该是孤立的)。
隔离的一种方法是为每个测试从头开始重新创建数据库(更多内容见下文)。
但也有其他方法。在这里你有一个删除的测试,应该删除它的条目,没有其他人接触过。那些。对于删除测试,创建一个条目。对于阅读测试 - 你的记录。那些。可以在数据级别隔离。
通常,数据库中的实体自然是独立对象树的根。例如,如果每个用户都创建了其他用户无法使用的自己的数据,那么您可以让每个测试代表单独的用户运行。然后将隔离测试数据。
很明显,这并不适用于所有情况,因为仍然存在共享的实体、各种字典等。要测试使用它们,您需要将数据库置于已知的稳定状态。这里也有选项:
第一种方式比较慢,因为 除了数据库本身,还需要创建数据。第二个有一个缺点,即并非所有内容都可以在没有提交的情况下进行测试(例如,在提交期间检查的数据库中的延迟约束)甚至在事务回滚之后 - 您无法查看数据库和什么那里出错了。
好吧,还有一个注意事项。测试单个方法(例如读取测试、删除测试)通常是个坏主意。行为需要被测试。这意味着您需要测试的不是阅读方法,而是行为,即 测试的本质应该是-“创建后可以读取对象”。这就是创造的意义,没有阅读,创造就无法测试,就像没有创造就不可能(嗯,或者这通常没有意义),就不可能测试阅读。删除也一样。不读就删的方法没有测试,没有意义。有必要测试“删除后,无法读取对象”(因此,在删除之前应该是可能的)。因此,在一次测试中测试创建、读取和删除是有意义的。