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

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

我应该通过扩展抽象类,去掉抽象方法,然后测试所有具体方法来测试抽象类吗?然后只测试我重写的方法,并在扩展抽象类的对象的单元测试中测试抽象方法

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

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

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

  1. 您正在专门化抽象对象,但所有客户端都将通过其基接口使用派生类

  2. 您正在使用抽象基类来消除设计中对象内的重复,客户机通过自己的接口使用具体实现


1解决方案-战略模式

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

你应该考虑把它变成一个真实的接口,将抽象类变为具体的,并在它的构造函数中获取这个接口的实例。然后,派生类就成为这个新接口的实现

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


解决方案2

如果您有第二种情况,那么您的抽象类将作为助手类工作

看看它包含的功能。查看是否可以将其中任何一个推到正在操纵的对象上,以最小化此重复。如果您还剩下什么,请考虑将其作为一个助手类,您的具体实现在其构造函数中采用该类并删除其基类

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


通常

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

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


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

可以让基类同时执行这两个角色。。。ie:它有一个公共接口,并且有受保护的助手方法。如果是这种情况,那么您可以将helper方法分解为一个类(scenario2),并将继承树转换为策略模式

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


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

前几天我遇到了一个使用抽象的情况,所以我想探究一下原因

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

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

我本可以编写一个“SettingsFileParser”,由3个类包装,然后委托给基类公开数据访问方法。我选择不这样做,因为这将导致3个派生类中包含更多的委托代码

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

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

在这一点上,我不再希望他们的公共接口包含设置访问器方法;因此,我将更改该类以封装设置解析器类,而不是从中派生

因此,抽象类是:目前我避免委托代码的一种方式,也是代码中提醒我以后更改设计的一个标记。我可能永远都无法到达它,所以它可能会活得很好。。。只有代码才能判断

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

我想象这样的规则定义了一个环境,可维护的代码生活在山谷中。当您添加新的行为时,就像雨落在代码上一样。最初你把它放在任何地方。。然后进行重构,让良好设计的力量推动行为,直到所有行为都陷入低谷

发表评论