如何将 TDD 应用于逐层封装设计?

How to apply TDD to a package-by-layer design?

我已经阅读了很多关于 TDD 的文章,尤其是关于各种实践和经验、该做和不该做的,在尝试将它应用到 Spring 引导后我仍然对几个方面感到困惑具有持久性和 REST 的应用程序,全部按功能打包。

Whosebug 上的许多博客文章和答案都建议我们应该测试接口而不是实现。 但是,例如史蒂夫·弗里曼和纳特·普赖斯 "Growing Object-Oriented Software, Guided by Tests" 中的例子,提出了一种完全不同的方法, 因为他们都主要测试实现,断言方法调用的数量等。所以我们的测试实际上变得依赖于实现本身。 当然,我们可以使用控制反转 - 通过构造函数传递依赖项并在我们的测试中模拟它们,但是模拟真的有意义吗,比方说,一个 CRUD 存储库?

也许我会给你一个简单的例子来更好地描述这种情况。 假设我们有一个多页的用户信息表单,每个表单页面都必须单独保存。

因此,信息包的计划结构可能是这样的,按照按功能包的方法:

com
.. example
.... user
...... information
........ basic
.......... + BasicInformationDto
.......... + BasicInformationService
.......... - BasicInformationServiceImpl
.......... - BasicInformationDao
.......... - BasicInformationRepository
........ additional
.......... + AdditionalInformationDto
.......... + AdditionalInformationService
.......... - AdditionalInformationServiceImpl
.......... - AdditionalInformationDao
.......... - AdditionalInformationRepository
........ + InformationRestController

+ is public and - is default access modifier

我的第一个想法:

  1. 编写一个简单的 REST 集成测试来测试由 InformationRestController.
  2. 用这些创建 InformationRestController 映射。
  3. 为 BasicInformationService 创建测试,创建一个 BasicInformationServiceImpl 对象,模拟 BasicInformationRepository 并测试存储库的保存方法是否被恰好调用一次。
  4. 为 BasicInformationService 创建 类, BasicInformationServiceImpl 和 BasicInformationRepository 与 基本信息道。
  5. 重构,实现细节,dto和dao 字段等

如果我以后决定使用存储库的#saveAndFlush 怎么办?一个单元测试因为这么小的改动就需要重构,这正常吗? 如果我只测试接口,我该如何测试具有不同依赖项的不同实现?

那么,如果 BasicInformationService 的保存方法仅将 DTO 对象映射到 DAO,然后使用 BasicInformationRepository 持久化它,那么如何使用 TDD 设计 BasicInformationService 的保存方法?

简而言之,BDD(行为驱动开发)是 TDD 的一种改进,从测试整个堆栈的验收测试开始,然后根据需要通过单元测试来测试驱动细节。 You can unit-test every class if you want, but you don't have to, and I don't where it doesn't add value. 在你给出的情况下,我怀疑不需要对大多数组件进行单元测试。

在执行 TDD/BDD 时以已知设计为目标是完全正常的,因为您的框架需要它,或者因为该设计已经在您程序的其他垂直部分中建立。只有最极端的 TDD 开发者才会在没有框架的情况下开始。您不想做的是构建一个您的框架 不需要 并且测试和重构还没有告诉您您需要的大型设计。