如何在执行BDD时重构和发现依赖关系

时间:2011-10-29 12:46:51

标签: refactoring tdd mocking bdd

做BDD意味着从上到下,所以我们首先编写顶级功能测试。现在要设置一个测试,你通常需要设置一些模拟而不是真正的deps。您如何知道需要什么样的家属,您需要什么样的服务?我无法弥补如何在这个级别上定义依赖项。在经典的TDD自下而上,它就像将现有的impl重构为依赖对象一样简单。

BDD和mocking是否意味着我们需要先构建完整的依赖图并进行大致设计?

那么重构呢?似乎没有地方可以将你的impl打破到依赖关系了,因为你已经定义了那些。

例如,我有一个定时缓存功能来实现。 我的测试应该是:

  • 如果未缓存,则通过存储调用返回值
  • 如果缓存,则返回没有存储空调的值
  • 超出超时时通过存储调用返回值

这是否意味着我需要确定所有潜在的依赖关系并准备好模拟?我应该验证从我的xall返回的值,还是只是预期的依赖性调用?

3 个答案:

答案 0 :(得分:4)

我有一种感觉,你可能会过分思考这一点。当事情看起来真的令人困惑时,我通常会提醒自己退后一步并检查基础知识。首先,我想只做满足要求所需的最低要求。我真的不在乎系统中是否存在许多我尚未意识到的依赖关系,因此我以最简单的方式编写测试。如果我看到需要一个依赖项,我会抛出一个mock并支持允许我的测试运行所需的最小接口。如果系统稍后需要扩展或支持其他依赖项,我可以扩展模拟,甚至用我编写的下一个测试的特定内容替换它。我也可以扩展测试,甚至编写一个新的测试来满足新的需求,因为它们对我来说已经知道和/或清楚。

重构只是一种说法,我打算改变一些东西。这可能是为了优化或改进设计,或者可能是因为我认为需要打破新的接口和依赖关系。在这种情况下,我首先将更改写入我的测试,或者甚至编写一个新的测试,可能会或可能不会替换现有的测试。完成后,我转到我的代码。

自上而下或自下而上,基本面基本相同。问题是,当依赖关系变得不言自明,然后确定一个处理它们的策略。当你觉得有必要打破一些东西时,我会建议一个非常短的尖峰。让这个想法成形,然后在你的眼前滚动它,同时向它提出问题。如果追求这个想法感觉正确,可以编写测试来处理你的尖峰给你带来的问题,然后从那里编写代码。

我有时会发现自己有了创造大量模拟的冲动,以应对我能想到的每一种可能的依赖性偶然事件,并且每当我回到重复“KISS”,“YAGNI”和“Make it”时工作,然后让它更好地工作“,在我的脑海里一遍又一遍,直到我得到的信息是我无法忍受被卡住,因为我试图想得太远。

无论您是TDD,BDD还是使用任何其他方法,保持简单和最小化是确保您不会立即解决太多问题的关键。这可能是最重要的事情要记住。如果看起来太难以想象,那么问问自己是否有“需求气味”,这表明你需要进一步分解。你能否把“故事”分解成一系列包含整体的小故事。

我知道我没有必要回答你直接提出的问题,但是我认为值得以这种方式写作,因为我的经验是,当任务很小时,依赖关系和重构变得不言而喻,仅限于单个特征或两个特征,而当任务过于笼统并且可以解释时会发生相反的情况。

答案 1 :(得分:1)

使用BDD,您不在单元级别工作(即一个公共类的一个公共方法)。您正在系统级别工作。因此,您需要模拟系统边界之外的所有内容。这意味着将模拟存储系统或外部系统:文件,数据库,服务等。

此外,你需要确保你的规范反复证明自己,所以像 TDD 一样,你会想要模仿任何非确定性元素。这尤其意味着时钟随机功能,但也可能意味着气象服务(当然与您想要模拟的无关元素重叠)或类似的东西。

至于如何发现它们,我看到了两种可能性,你可以使用它们。首先是绘制一个系统图,它基本上是一个表示 SUT 中所有内容的框。然后绘制你知道你需要的任何属于上述类别的东西。这就是你需要模仿的东西。

另一种技术是进行编码,直到你陷入必须模拟的依赖关系,然后使用 Bridge Pattern 重构,以免依赖它。

希望这有帮助。

答案 2 :(得分:0)

不要用顶级驱动设计(集成?) - 测试。

使用它们来了解您的功能何时完成,并使用标准TDD实践驱逐代码的内部。

有点短,但我希望你理解我的意思。