unit/integration 测试可以没有断言吗?那么这个特殊案例呢?
Can unit/integration tests have no assert? And what about this particular case?
场景:我有一些集成测试。其中之一测试删除。所以我已经完成了:
@Test
@Order(6)
public void delete() {
rfxCriteriRepository.delete(rfxCriteriEconomico.getIdrfxcriteri());
}
简单地测试方法是否抛出异常。然后,为了确保删除成功,我添加了:
@Test
@Order(7)
public void getDelete() {
RfxCriteri rfxCriteriEconomicoDb = rfxCriteriRepository.findByIdrfxcriteri(
rfxCriteriEconomico.getIdrfxcriteri()
);
Assertions.assertNull(rfxCriteriEconomicoDb);
}
我的想法是,第一个测试测试代码。如果代码写得好,它应该不会抛出异常,并且测试通过。第二个测试测试删除是否有效地从数据库中删除了条目。恕我直言,它们是两个独立的测试。
你认为每个测试都必须有一个assert
吗?或者你认为这两个测试应该是一个独特的测试?有人能给我指出一些关于单元测试的指南吗?
如果你没有断言,那么你就没有测试。
测试意味着您需要能够设置一些数据,运行 被测方法,然后测试结果,这就是断言发挥作用的地方。
以您的特定示例为例,您的代码没有抛出错误并不意味着删除确实有效。唯一的测试方法是去数据库检查,这意味着你有一个集成测试,而不是单元测试。
写 "tests" 因为代码中没有错误而成功是没有意义的,你实际上并没有测试任何东西,有无数种方法可以编写不会失败但实际上没有做的代码任何东西。
测试边缘情况 - 你传递 0 作为 id,你传递 null。
测试当数据库中不存在 id 时会发生什么。
测试当 id 存在但有链接数据时会发生什么,这可能意味着您需要先删除其他内容。
测试快乐路径场景。
如果您预计在某些情况下会出现某些错误,那么也请测试这些错误。
您可以编写大量测试,确保它们可以 运行 单独,它们不共享状态等。
每个测试都需要以某种形式评估结果。但是,在某些情况下,这可能是隐含的。例如,许多测试框架将在测试执行期间发生的未捕获和意外异常视为测试失败。当您使用这样的框架并且您有一个测试,其中唯一重要的是没有引发异常,那么您可以在没有任何显式断言语句的情况下编写该测试 - 框架为您执行断言。
但是,以未出现异常为唯一成功标准的测试并不常见。但是,在某些情况下这是有意义的:以经典示例为例,其中通过传递三个边的长度 a
、b
和 c
来定义三角形。然后,一种极端情况是两条边之和等于第三条边,如 a == b + c
。然后,你可以有三个测试用例来测试边界:
- 一种情况,其中两条边的长度之和刚好超过(一个 epsilon)第三条边的长度:
a + epsilon == b + c
- 在任何情况下这都是一个有效的三角形。
- 一种情况,其中一侧比其他两条的总和长(同样是 epsilon),例如
a - epsilon == b + c
。这应该引发异常,因为这不是有效的三角形。
- 终于出现了
a == b + c
的情况。
假设在您的代码中情况 3 的情况将产生退化但有效的三角形。然后,拥有一个仅创建此三角形的测试用例就有意义了——确保没有抛出异常。请记住,您的测试也可以被视为您的代码的文档:这样的测试很好地记录了您的代码允许创建这样的三角形。在这种情况下,测试用例当然应该被实现和命名,以便意图变得清晰,例如 "constructing_aDegenerateTriangle_shallSucceedWithoutExceptionRaised".
现在,来到你问题的第二部分,在你的具体情况下,没有断言的测试是否有意义。在这种情况下,我也只会将 JBNizet 提到的组合测试视为有用(它会删除然后检查该条目是否已真正删除)。您可以单独进行两个测试 - 但是对断言的一个测试将是多余的,因为测试框架的隐式断言检查无论如何都会在两个测试用例中完成。
场景:我有一些集成测试。其中之一测试删除。所以我已经完成了:
@Test
@Order(6)
public void delete() {
rfxCriteriRepository.delete(rfxCriteriEconomico.getIdrfxcriteri());
}
简单地测试方法是否抛出异常。然后,为了确保删除成功,我添加了:
@Test
@Order(7)
public void getDelete() {
RfxCriteri rfxCriteriEconomicoDb = rfxCriteriRepository.findByIdrfxcriteri(
rfxCriteriEconomico.getIdrfxcriteri()
);
Assertions.assertNull(rfxCriteriEconomicoDb);
}
我的想法是,第一个测试测试代码。如果代码写得好,它应该不会抛出异常,并且测试通过。第二个测试测试删除是否有效地从数据库中删除了条目。恕我直言,它们是两个独立的测试。
你认为每个测试都必须有一个assert
吗?或者你认为这两个测试应该是一个独特的测试?有人能给我指出一些关于单元测试的指南吗?
如果你没有断言,那么你就没有测试。 测试意味着您需要能够设置一些数据,运行 被测方法,然后测试结果,这就是断言发挥作用的地方。
以您的特定示例为例,您的代码没有抛出错误并不意味着删除确实有效。唯一的测试方法是去数据库检查,这意味着你有一个集成测试,而不是单元测试。
写 "tests" 因为代码中没有错误而成功是没有意义的,你实际上并没有测试任何东西,有无数种方法可以编写不会失败但实际上没有做的代码任何东西。
测试边缘情况 - 你传递 0 作为 id,你传递 null。 测试当数据库中不存在 id 时会发生什么。 测试当 id 存在但有链接数据时会发生什么,这可能意味着您需要先删除其他内容。 测试快乐路径场景。
如果您预计在某些情况下会出现某些错误,那么也请测试这些错误。
您可以编写大量测试,确保它们可以 运行 单独,它们不共享状态等。
每个测试都需要以某种形式评估结果。但是,在某些情况下,这可能是隐含的。例如,许多测试框架将在测试执行期间发生的未捕获和意外异常视为测试失败。当您使用这样的框架并且您有一个测试,其中唯一重要的是没有引发异常,那么您可以在没有任何显式断言语句的情况下编写该测试 - 框架为您执行断言。
但是,以未出现异常为唯一成功标准的测试并不常见。但是,在某些情况下这是有意义的:以经典示例为例,其中通过传递三个边的长度 a
、b
和 c
来定义三角形。然后,一种极端情况是两条边之和等于第三条边,如 a == b + c
。然后,你可以有三个测试用例来测试边界:
- 一种情况,其中两条边的长度之和刚好超过(一个 epsilon)第三条边的长度:
a + epsilon == b + c
- 在任何情况下这都是一个有效的三角形。 - 一种情况,其中一侧比其他两条的总和长(同样是 epsilon),例如
a - epsilon == b + c
。这应该引发异常,因为这不是有效的三角形。 - 终于出现了
a == b + c
的情况。
假设在您的代码中情况 3 的情况将产生退化但有效的三角形。然后,拥有一个仅创建此三角形的测试用例就有意义了——确保没有抛出异常。请记住,您的测试也可以被视为您的代码的文档:这样的测试很好地记录了您的代码允许创建这样的三角形。在这种情况下,测试用例当然应该被实现和命名,以便意图变得清晰,例如 "constructing_aDegenerateTriangle_shallSucceedWithoutExceptionRaised".
现在,来到你问题的第二部分,在你的具体情况下,没有断言的测试是否有意义。在这种情况下,我也只会将 JBNizet 提到的组合测试视为有用(它会删除然后检查该条目是否已真正删除)。您可以单独进行两个测试 - 但是对断言的一个测试将是多余的,因为测试框架的隐式断言检查无论如何都会在两个测试用例中完成。