在进行 TDD 时,何时实施新的模拟依赖项?
While doing TDD, when to implement a new mocked dependency?
关于采用由外向内方法的 TDD,有一个问题我找不到答案:
我实现了一个新单元 (A),为它编写了一个测试,这个单元需要一个尚不存在的依赖项 (B)。在我的测试中很容易模拟这种依赖关系,但我在生产代码中做什么?
我是否先实施 (B) 并同时让我对 (A) 的测试失败,因为我还没有继续实施它以使其测试通过?
或者我是先完成 (A),同时让 (B) 的测试失败,因为它例如只是 returns "empty" 个对象,而不是实际执行其规范要求它执行的操作?
或者我应该让 (B) 的测试暂时检查它 returns "empty" 对象,同时我继续实施 (A) - 尽管这实际上不是 (B) 的规范?
TDD 的基本策略是让你的所有测试都通过,除了你现在正在处理的测试。让 (A) 的测试通过,然后再担心 (B)。
您为 class (A) 及其复杂的依赖项 (B) 编写测试和代码的顺序是
- 为 (A) 写一个测试。 [套件是红色的。]
- 开始实施足够的 (A) 以使您刚刚编写的测试通过。发现你需要(B)。 [套件是红色的。]
- 模拟 (B)。 [套件是红色的。]
- 使您刚刚编写的(A)测试通过。 [套房是绿色的。啊!]重构。
- 如果您还没有在 (A) 的良好停止点,返回顶部并重复,直到您在 (A) 的良好停止点。
- 为 (B) 编写一个测试,要求 (B) 执行 (B) 的 mock 所做的部分或全部操作。 [套件是红色的。]
- 使您刚刚编写的测试通过。 [套房是绿色的。啊!]重构。
- 如果您还没有复制 (B) 的模拟在 (B) 的测试和代码中所做的所有事情,请返回两个步骤并重复,直到您复制了 (B) 的所有模拟) 确实如此。
此时您可以选择在 (A) 或 (B) 上做更多的工作,或者开始新的事情。
尽管此策略使您的测试始终通过,但它不能确保您的应用程序立即执行有用的操作。确保您的应用程序最终做一些有用的事情的方法超出了 TDD:首先编写一个验收测试(它在没有模拟的情况下针对整个应用程序运行)和 TDD,直到验收测试和您的单元测试全部通过。 (有关更多信息,请参阅 bdd。)
验收测试(或其他集成测试)还确保您在模拟 classes 的测试和代码中正确复制模拟。
另请注意,跟踪您已考虑但尚未实施的要求,或者您 'implemented' 仅在模拟中需要在测试中实施和编写代码的要求至关重要嘲笑的依赖。这就是为什么 TDD By Example 和其他关于如何完成 TDD 的例子如此多地谈论实际或心理待办事项列表。对于具有模拟依赖项 (B) 的 class (A),在编写模拟后,您可以返回处理 (A) 或在 (B) 中实现您刚刚对模拟所做的操作.无论哪种方式,您都必须跟踪您选择不做的事情,直到您准备好返回并做它。
我建议只有在代码中确实需要 B 时才依赖 B。如果 A 中没有任何内容依赖于 B,为什么要在这个阶段添加它?
如果您的代码现在确实需要 B,为什么不开始实现您现在需要的 B 的功能?
当然,如果 B 没有任何逻辑,您也可以在代码中使用虚拟对象代替 B。否则我会开始实施 B.
关于采用由外向内方法的 TDD,有一个问题我找不到答案:
我实现了一个新单元 (A),为它编写了一个测试,这个单元需要一个尚不存在的依赖项 (B)。在我的测试中很容易模拟这种依赖关系,但我在生产代码中做什么?
我是否先实施 (B) 并同时让我对 (A) 的测试失败,因为我还没有继续实施它以使其测试通过?
或者我是先完成 (A),同时让 (B) 的测试失败,因为它例如只是 returns "empty" 个对象,而不是实际执行其规范要求它执行的操作?
或者我应该让 (B) 的测试暂时检查它 returns "empty" 对象,同时我继续实施 (A) - 尽管这实际上不是 (B) 的规范?
TDD 的基本策略是让你的所有测试都通过,除了你现在正在处理的测试。让 (A) 的测试通过,然后再担心 (B)。
您为 class (A) 及其复杂的依赖项 (B) 编写测试和代码的顺序是
- 为 (A) 写一个测试。 [套件是红色的。]
- 开始实施足够的 (A) 以使您刚刚编写的测试通过。发现你需要(B)。 [套件是红色的。]
- 模拟 (B)。 [套件是红色的。]
- 使您刚刚编写的(A)测试通过。 [套房是绿色的。啊!]重构。
- 如果您还没有在 (A) 的良好停止点,返回顶部并重复,直到您在 (A) 的良好停止点。
- 为 (B) 编写一个测试,要求 (B) 执行 (B) 的 mock 所做的部分或全部操作。 [套件是红色的。]
- 使您刚刚编写的测试通过。 [套房是绿色的。啊!]重构。
- 如果您还没有复制 (B) 的模拟在 (B) 的测试和代码中所做的所有事情,请返回两个步骤并重复,直到您复制了 (B) 的所有模拟) 确实如此。
此时您可以选择在 (A) 或 (B) 上做更多的工作,或者开始新的事情。
尽管此策略使您的测试始终通过,但它不能确保您的应用程序立即执行有用的操作。确保您的应用程序最终做一些有用的事情的方法超出了 TDD:首先编写一个验收测试(它在没有模拟的情况下针对整个应用程序运行)和 TDD,直到验收测试和您的单元测试全部通过。 (有关更多信息,请参阅 bdd。)
验收测试(或其他集成测试)还确保您在模拟 classes 的测试和代码中正确复制模拟。
另请注意,跟踪您已考虑但尚未实施的要求,或者您 'implemented' 仅在模拟中需要在测试中实施和编写代码的要求至关重要嘲笑的依赖。这就是为什么 TDD By Example 和其他关于如何完成 TDD 的例子如此多地谈论实际或心理待办事项列表。对于具有模拟依赖项 (B) 的 class (A),在编写模拟后,您可以返回处理 (A) 或在 (B) 中实现您刚刚对模拟所做的操作.无论哪种方式,您都必须跟踪您选择不做的事情,直到您准备好返回并做它。
我建议只有在代码中确实需要 B 时才依赖 B。如果 A 中没有任何内容依赖于 B,为什么要在这个阶段添加它?
如果您的代码现在确实需要 B,为什么不开始实现您现在需要的 B 的功能?
当然,如果 B 没有任何逻辑,您也可以在代码中使用虚拟对象代替 B。否则我会开始实施 B.