每层集成测试是一个好习惯吗?

我有一个使用spring-mvc的应用程序,基本上我们有一个表示层(控制器),服务层(业务单元,帮助器),集成层和数据访问层(jdbc / jpa存储库),我们希望确保使用未来的测试除了代码之外不会破坏以前工作的任何东西,为此我们使用unit testing(mockito)和集成测试(spring-test,spring-test-mvc)。

每个类/组件进行unit testing,基本上我们试图对输入输入和这些组件中的可能流进行良好的覆盖,这个动作工作正常,这里没有疑问,因为unit testing是关于确保单元按预期工作。

集成测试是不同的故事和非常有争议的一个,因为现在我们有时使用我们用于设计unit testing的相同场景,但使用真实平台等可以使用整个系统,但我对这里的最佳实践有疑问。

  1. 由于我们有一个控制器,服务,数据层一种方法是每层IT,例如我们有UserService类,我们将有UserServiceTest,它将是Unit测试和UserServiceIT,但可维护性不理想,我觉得有时候我们会重复相同的测试场景,但现在使用真实系统。 这种做法真的有意义吗或在哪种情况下这是有道理的? 如果我们已经在课堂上有100%的测试覆盖率进行unit testing,为什么我们需要IT来实现这一目标,我们认为这只是为了确保真正的组件能够启动?,有意义的是拥有所有相同的场景或哪些是一个很好的标准来决定?

  2. 其他方法只是通过集成测试来处理最重要的测试用例,而只是来自控制器层,这意味着调用REST服务并validationJSON输出。 这就够了吗?我们不需要在其他层中validation更多东西吗? 我知道调用真正的REST api将在所有层(控制器,服务,dao)下面使用但这已经足够了吗? 你会在这里说些什么吗?

  3. 如果我们有一个帮助类我不认为有单位和IT是有意义的,因为大多数方法只有一个目的我觉得unit testing在这里就够了,你认为相同吗?

  4. 数据层中的某些类可以使用Criteria API,QueryDSL用于那些我使用IT的人,因为在某些情况下unit testing非常困难,这是一个有效的理由吗?

我正在努力获得最好的方法,技巧和实践,使确保系统完整性的任务成为真正有价值的过程,同时牢记这些东西的可维护性。

您需要触摸应用程序所需的整个测试策略。 测试不仅涉及覆盖范围和层次。 例如:

我们希望确保使用测试,未来添加代码不会破坏以前工作的任何东西,为此我们使用unit testing(mockito)和集成测试(spring-test,spring-test-mvc)。

这就是你实际支持回归测试的方式 ,这是一种类型。 如果我们看一下(详细的)测试金字塔

测试金字塔

很容易看出集成测试占了很大比例(建议5-15%)。 集成是跨层的,但也是跨组件API。 您的业​​务组件自然会存在于同一层中,但您仍然需要确保它们彼此之间的工作正常。 拥有mSOA将推动您支持如此广泛的接口集成测试。

我同意你的看法

集成测试是不同的故事,非常值得商榷

一些专家甚至建议您只需要保留unit testing和GUI E2E 。 恕我直言,没有严格的最佳做法 – 只有好的做法。 如果您对权衡取舍感到满意,请使用适合您情况的产品。

我觉得有时我们重复相同的测试场景,但现在使用真实的系统。 这种做法真的有意义吗或在哪种情况下这是有意义的? 如果我们已经在课堂上有100%的测试覆盖率进行unit testing,为什么我们需要IT来实现这一目标,那么我们认为这只是为了确保真正的组件能够启动吗? 有没有理由拥有所有相同的场景或哪个是一个很好的标准来决定?

看起来您需要在这些场景中画一条线。 长话短说 – unit testing和模拟对象自然地结合在一起。 组件测试需要一些真实的系统行为,它可用于检查在各个单元或子系统组件之间传递的数据的处理 – 如组件/服务DB或非单元级任务的消息传递。

来自控制器层,这意味着调用REST服务并validationJSON输出。 这够了吗?我们不需要在其他层中validation更多东西吗? 我知道调用真正的REST api将在所有层(控制器,服务,dao)下面使用但这已经足够了吗?

不完全正确 – 测试表示层也会运行底层…所以为什么还要考虑所有其他测试? 如果你对这种方法没问题–Selenium团队建议采用这种数据库validation方法。

如果你在这里谈论Beans和ViewHelpers

我们有一个帮助类我不认为有单位和IT有意义,因为大多数方法只有一个目的我觉得unit testing就够了,你觉得一样吗?

你需要单位和IT,因为所有原因都适用于其他组件。 承担单一责任并不否认需要进行IT测试。

在某些情况下进行unit testing是非常困难的,这是一个有效的理由吗?

所有封装的私有(和静态)类,方法,属性等也是如此。但是有一种方法可以测试它们 – 比如reflection 。 这当然是针对特殊情况的unit testing遗留代码或您无法更改的API。 如果您需要自己的代码,可能缺乏可测试性指向设计气味。

基于最近测试Java EE 7和基于Spring的代码库的经验,我建议的方法是:

使用每个function集成测试,并避免unit testing和模拟。 每个集成测试应涵盖所有层的代码,从表示层到基础结构层(这个包含可重用的组件,这些组件不是应用程序或域特定的,但适用于所选的体系结构)。

大多数集成测试应基于实际业务需求和输入数据。 根据每次执行集成测试套件生成的代码覆盖率报告,可以创建其他部分来执行代码库的剩余部分。

因此,假设通过集成测试实现“完全”代码覆盖,并且它们运行得足够快,那么根本没有太多理由进行unit testing。 我的经验是,在编写unit testing时,开发人员倾向于使用过多的模拟,经常创建脆弱的测试来validation不必要的实现细节。 此外,unit testing永远不会提供与集成测试相同的置信度,因为它们通常不包括数据库查询,ORM映射等内容。

unit testing适用于您对类和组件所做的操作。 其目的是:

  • 编写代码(TDD)。
  • 说明代码的使用,并使其随着时间的推移和变化而持续。
  • 尽可能多地覆盖边境案例。

当您遇到某些特定用法或参数的问题时,首先使用新测试重现它,然后进行修复。

只有在需要测试类或组件独立行为并且从应用程序外部(例如电子邮件服务器)生成模拟function时,才应使用模拟。 当代码已经被覆盖并且模拟与其他类型的测试(例如集成测试)的责任重叠时,它是过度和无用的。

既然你知道每一段代码都有效,那么这些代码如何协同工作? 这就是集成测试的地方,该测试涉及组件在各种条件和环境中如何相互作用。 UT和IT之间的差异有时很小:例如数据访问层的测试。 集成测试用于与unit testing相同的目的,但在更高级别,更少primefaces,用于说明API,服务的用例……

你怎么称呼“整合层”?

表示层测试是function测试的责任,而不是单元或集成。

您也没有谈论性能测试。

最后,目标是将所有代码与测试一起编写,在使用新测试进行复制后修复错误,并在所有可能条件(OS,数据库,浏览器……)中累积所有类型的测试。 因此您可以通过以下方式validation整体测试质

  • 计算覆盖范围的工具。 您可能需要检测代码以评估function测试的覆盖范围或使用高级JDK工具。
  • 由于缺乏对某些组件,服务的测试而导致的错误数量……

我通常认为在阅读它们时,一堆测试是好的,这使我立即毫不怀疑如何使用它们所涵盖的代码,并对其合同,function,输入和输出,历史和用例的可耗性,强度和稳定性充满信心。错误管理和报告。

覆盖范围也不是一件很重要的事情,但如果你专注于它们的质量,那么最好少做一些测试:线程安全,由无序方法和类组成,测试真实条件(没有“如果测试条件”黑客攻击)。

回答你的问题:我会说,考虑到上述考虑因素,你不必每层编写一次集成测试,因为你宁愿选择不同的测试策略(单元,集成,function,性能,冒烟,模拟…… )为每一层。