一尘不染

如何对抽象类进行单元测试:用存根扩展?

javascript

我想知道如何对抽象类和扩展抽象类的类进行单元测试。

我是否应该通过扩展抽象类、存根抽象方法来测试抽象类,然后测试所有具体方法?然后只测试我覆盖的方法,并在单元测试中测试扩展我的抽象类的对象的抽象方法?

我是否应该有一个抽象测试用例可以用来测试抽象类的方法,并在我的测试用例中为扩展抽象类的对象扩展这个类?

请注意,我的抽象类有一些具体的方法。


阅读 119

收藏
2022-05-29

共1个答案

一尘不染

有两种使用抽象基类的方式。

  1. 您正在专门化您的抽象对象,但所有客户端都将通过其基接口使用派生类。
  2. 您正在使用抽象基类来排除设计中对象内的重复,并且客户通过他们自己的接口使用具体实现。!

解决方案 1 - 策略模式

选项1

如果您遇到第一种情况,那么您实际上有一个由派生类正在实现的抽象类中的虚拟方法定义的接口。

您应该考虑使它成为一个真正的接口,将您的抽象类更改为具体的,并在其构造函数中获取该接口的一个实例。然后,您的派生类将成为这个新接口的实现。

爱电机

这意味着您现在可以使用新接口的模拟实例测试您以前的抽象类,并通过现在的公共接口测试每个新实现。一切都很简单且可测试。


解决方案 2

如果您有第二种情况,那么您的抽象类正在充当帮助类。

抽象助手

看看它包含的功能。看看是否可以将其中的任何一个推到正在被操纵的对象上以最小化这种重复。如果您还有任何剩余,请考虑使其成为您的具体实现在其构造函数中采用的辅助类并删除它们的基类。

电机助手

这再次导致了简单且易于测试的具体类。


作为一项规则

喜欢简单对象的复杂网络,而不是复杂对象的简单网络。

可扩展可测试代码的关键是小型构建块和独立布线。


更新:如何处理两者的混合物?

可以有一个基类来执行这两个角色......即:它有一个公共接口,并且有受保护的辅助方法。如果是这种情况,那么您可以将辅助方法分解为一个类(场景 2)并将继承树转换为策略模式。

如果你发现你有一些你的基类直接实现的方法,而另一些是虚拟的,那么你仍然可以将继承树转换为策略模式,但我也会把它作为一个很好的指标,表明职责没有正确对齐,并且可能需要重构。


更新 2:抽象类作为垫脚石 (2014/06/12)

前几天我遇到了一个使用abstract的情况,所以我想探讨一下原因。

我们的配置文件有一个标准格式。这个特殊的工具有 3 个配置文件都采用这种格式。我希望每个设置文件都有一个强类型类,因此,通过依赖注入,一个类可以请求它关心的设置。

我通过拥有一个抽象基类来实现这一点,该基类知道如何解析设置文件格式和暴露这些相同方法的派生类,但封装了设置文件的位置。

我本可以编写一个“SettingsFileParser”,将 3 个类包装起来,然后委托给基类以公开数据访问方法。我选择不这样做因为它会导致 3 个派生类中的委托代码比其他任何东西都多。

但是…随着此代码的发展以及这些设置类中的每一个的使用者变得更加清晰。每个设置用户都会要求一些设置并以某种方式对其进行转换(因为设置是文本,他们可以将它们包装在将它们转换为数字等的对象中)。发生这种情况时,我将开始将此逻辑提取到数据操作方法中,并将它们推回到强类型设置类中。这将为每组设置带来更高级别的界面,最终不再意识到它正在处理“设置”。

此时,强类型设置类将不再需要公开底层“设置”实现的“getter”方法。

那时我不再希望他们的公共接口包含设置访问器方法;所以我将改变这个类来封装一个设置解析器类,而不是从它派生。

因此,Abstract 类是:一种让我暂时避免使用委托代码的方法,以及在代码中提醒我稍后更改设计的标记。我可能永远也达不到它,所以它可能会活很长一段时间……只有代码才能说明。

我发现这适用于任何规则......比如“没有静态方法”或“没有私有方法”。它们表明代码中有异味……这很好。它让您一直在寻找您错过的抽象......同时让您继续为您的客户提供价值。

我想象像这样的规则定义了一个景观,可维护的代码存在于山谷中。当您添加新行为时,就像雨点落在您的代码上一样。最初你把它放在它所在的任何地方......然后你重构以允许良好设计的力量推动行为,直到它全部结束在山谷中。

2022-05-29