【问题标题】:Integration tests, but how much? [closed]集成测试,但是多少? [关闭]
【发布时间】:2017-07-18 07:11:54
【问题描述】:

我团队最近的一场辩论让我感到好奇。基本主题是我们应该用功能/集成测试覆盖多少和什么(当然,它们不一样,但这个例子是假的,这无关紧要)。

假设您有一个“控制器”类,例如:

public class SomeController {
    @Autowired Validator val;
    @Autowired DataAccess da;
    @Autowired SomeTransformer tr;
    @Autowired Calculator calc;

    public boolean doCheck(Input input) {
        if (val.validate(input)) {
             return false;
        }

        List<Stuff> stuffs = da.loadStuffs(input);
        if (stuffs.isEmpty()) {
             return false;
        }

        BusinessStuff businessStuff = tr.transform(stuffs);
        if (null == businessStuff) {
            return false;
        }

       return calc.check(businessStuff);
    }
}

我们确实需要大量的单元测试(例如,如果验证失败,或者数据库中没有数据,......),这是毫无疑问的。

我们的主要问题是我们不能达成一致意见的是集成测试应该涵盖多少:-)

我支持我们的目标是减少集成测试(测试金字塔)。我要涵盖的只是一条快乐-不快乐的路径,执行从最后一行返回,只是为了看看我是否将这些东西放在一起它不会爆炸。

问题在于,要判断为什么测试结果为假并不那么容易,这让一些人对此感到不安(例如,如果我们只检查返回值,隐藏了测试是绿色的,因为有人更改了验证并返回 false)。当然,是的,我们可以涵盖所有情况,但恕我直言,这将是一个严重的矫枉过正。

对于这类问题,有没有人有好的经验法则?还是推荐?读?讲话?博文?有什么话题吗?

提前非常感谢!

PS:抱歉这个丑陋的例子,但很难将特定的代码部分翻译成一个例子。是的,人们可以争论抛出异常/使用不同的返回类型/等等。但由于外部依赖,我们的手或多或少受到了束缚。

【问题讨论】:

  • 我投票决定将此问题作为离题结束,因为它属于 softwareengineering.stackexchange.com 。然而,这是一个非常好的问题!
  • 我不同意搁置,因为我要求提供一些参考资料(文章、对 Google 隐藏的神奇 Martin Fowler 卷轴、关于上述情况的具体文章等)。无论如何,我会将问题添加到 se.so.com 并在此处留下链接。

标签: java unit-testing testing automated-tests integration-testing


【解决方案1】:

如果您遵循以下规则,很容易找出测试应该放在哪里:

  • 我们在单元测试级别检查逻辑,并检查是否在组件或系统级别调用逻辑
  • 我们不使用模拟框架(mockito、jmock 等)。

让我们开始吧,但首先让我们就术语达成一致:

  • 单元测试 - 单独检查一个方法、一个类或其中的几个
  • 组件测试 - 初始化应用程序的一部分,但不将其部署到应用程序服务器。示例可能是 - 在测试中初始化 Spring 上下文。
  • 系统测试 - 需要在 App Server 上进行完整部署。示例可能是:向远程服务器发送 HTTP REST 请求。

如果我们建立一个平衡的金字塔,我们最终会在单元和组件级别进行大多数测试,而其中很少有系统测试会留给系统测试。这很好,因为较低级别的测试更快更容易。为此:

  • 我们应该将业务逻辑放在尽可能低的位置(最好在域模型中),因为这样我们可以轻松地单独测试它。每次您遍历对象集合并在其中设置条件时 - 理想情况下应该转到域模型。
  • 但逻辑工作的事实并不意味着它被正确调用。那就是您需要组件测试的地方。初始化您的控制器以及服务和 DAO,然后调用它一两次以查看是否调用了逻辑。

示例:用户名不能超过 50 个符号,只能有拉丁文以及一些特殊符号。

  • 单元测试 - 使用正确和错误的用户名创建用户,检查是否引发异常,反之亦然 - 有效名称正在通过
  • 组件测试 - 检查当您将无效用户传递给控制器​​时(如果您使用 Spring MVC - 您可以使用 MockMVC 执行此操作)它会引发错误。在这里您只需要传递一个用户 - 现在所有规则都已经检查过了,在这里您只想知道是否调用了这些规则。
  • 系统测试 - 实际上在这种情况下您可能不需要它们..

Here is a more elaborate example 了解如何实现平衡金字塔。

【讨论】:

  • 感谢您的示例,我会阅读链接的博客文章。
  • 您提到只为无效用户编写组件测试。您是否建议还为快乐路径场景编写组件测试?
  • 是的,但那将是全球幸福之路。它不仅涵盖了愉快的验证,还涵盖了该特定端点或高级方法的愉快的数据库通信、愉快的计算等。通常为每个端点编写 1 个快乐组件测试就足够了。但是如果更多的逻辑被放入 Controller 或 Service 中,你将不得不将其扩展到更多的情况。更高层次的逻辑越多,需要的测试就越多。
  • @StanislavBashkyrtsev 为什么说“我们不使用模拟框架”?当您不模拟组件以确保它们被使用时,您怎么知道在系统测试中调用了逻辑?
  • 你不需要确保组件的使用——你需要确保最终结果是正确的。如果您保留了一些数据,那么您可以打开包含该数据的页面并查看它的存在。
【解决方案2】:

一般来说,我们在应用程序的每个起点(假设每个控制器)都编写一个集成测试。我们验证了一些快乐的流程和一些错误的流程,并通过几个断言让我们放心,我们没有破坏任何东西。

但是,我们也会在较低级别编写测试以响应回归或当多个类涉及到一个复杂的行为时。

我们使用集成测试主要是为了捕捉以下类型的回归:

  1. 重构错误(单元测试未发现)。

对于重构问题,对应用程序的大部分进行测试的几个 IT 测试就绰绰有余了。重构通常会影响大部分类,因此这些测试会暴露一些问题,例如在某处使用错误的类或参数。

  1. 注入问题的早期检测(上下文未加载,Spring)

注入问题通常是由于缺少注释或 XML 配置中的错误而发生的。运行并设置整个上下文(除了模拟后端)的第一个集成测试每次都会捕获这些。

  1. 如果不控制所有输入,几乎无法测试超级复杂逻辑中的错误

有时您的代码分布在多个类中,需要过滤、转换等,有时没有人真正了解发生了什么。更糟糕的是,在实时系统上进行测试几乎是不可能的,因为底层数据源无法轻易提供触发错误的确切场景。

对于这些情况(一旦发现),我们添加了一个新的集成测试,我们向系统提供导致错误的输入,然后验证它是否按预期执行。在大量代码更改之后,这让您高枕无忧。

【讨论】:

  • 是的,我们在同一条轨道上。神奇的词是“验证一些快乐的流程和一些错误的流程”。我相信这是我们集成测试的主要用途,它们不应该涵盖所有极端情况。它们太贵了。谢谢。
  • 同意,您不需要测试所有极端情况——在集成测试中添加极端情况的唯一原因是它是否经常导致回归
猜你喜欢
  • 2011-05-15
  • 2016-07-12
  • 2010-12-03
  • 2020-10-27
  • 1970-01-01
  • 2010-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多