当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何对抽象类进行单元测试:使用存根扩展?》经验,为你挑选了6个好方法。

我想知道如何对抽象类进行单元测试,以及扩展抽象类的类.

我应该通过扩展它来测试抽象类,删除抽象方法,然后测试所有具体方法吗?然后只测试我覆盖的方法,并在单元测试中测试扩展我的抽象类的对象的抽象方法?

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

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



1> Nigel Thorne..:

有两种方法可以使用抽象基类.

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

    您正在使用抽象基类来分解设计中对象内的重复,并且客户端通过自己的接口使用具体实现.


解决方案1 ​​ - 战略模式

选项1

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

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

IMOTOR

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


解决方案2

如果你有第二种情况,那么你的抽象类就是一个帮助类.

AbstractHelper

看看它包含的功能.查看是否可以将其中任何一个推送到正在操作的对象上以最小化此重复.如果你还有任何东西,那么看看它是否是一个帮助类,你的具体实现将在它们的构造函数中删除它们的基类.

电机助手

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


作为规则

在简单的复杂对象网络上支持复杂的简单对象网络.

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


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

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

如果您发现基类直接实现某些方法而其他方法是虚拟的,那么您仍然可以将继承树转换为策略模式,但我也会将其视为责任未正确对齐的良好指标,并且可能需要重构.


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

前几天我使用抽象的情况,所以我想探究原因.

我们的配置文件有标准格式.这个特殊工具有3个配置文件,全部采用该格式.我想为每个设置文件设置一个强类型类,因此,通过依赖注入,类可以请求它关心的设置.

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

我本可以编写一个"SettingsFileParser",将3个类包装起来,然后委托给基类来公开数据访问方法.我选择不这样做因为这将导致3派生类更代表团在他们的代码比什么都重要.

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

此时,强类型设置类将不再需要暴露底层"设置"实现的"getter"方法.

那时我不再希望他们的公共接口包含设置访问器方法; 所以我将更改此类以封装设置解析器类而不是从它派生.

因此,Abstract类是:我现在可以避免委托代码的方法,以及代码中的标记,以提醒我稍后更改设计.我可能永远不会达到它,所以它可能活得很好......只有代码才能说明.

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

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


单独为规则+1,"在简单的复杂对象网络上支持复杂的简单对象网络".
我无法克服这实际上有多好的答案.这完全改变了我对抽象类的思考方式.谢谢奈杰尔.
这是一个很好的答案.比评价最高的要好得多.但是我想只有那些*真的想要编写可测试代码的人才会欣赏它.. :)
很好的答案.绝对要考虑的事情......但不是你所说的基本上归结为不使用抽象类?
哦不..另一个原则我不得不重新思考!谢谢(现在都是讽刺的,非讽刺的,因为我已经吸收它并感觉自己是一个更好的程序员)
"没有私人方法"?那是我第一次读这篇文章.那是真的吗?
@loeschg存在抽象类来解决一些设计问题.由于我在我的代码中重视可测试性,我发现抽象类会导致我付出我不愿意付出的代价.我提出了更可测试的替代设计.我发现"可测性"是解耦的一个很好的替代指标.
是的.:)有更好的方法来构建代码.
@SoMoS我打算说抽象类可能对暴露API有用,所以其他人可以从它们派生并有可用的辅助方法,或者确保回调以正确的顺序发生......但是当我想到替代方案时我无法不能证明这一点.为了强制执行订单,我应该实现其他人调用的接口.对于辅助方法,提供辅助对象或丰富域对象.*简短回答......没有*
@Korey - 实际上你会有一个chassee,你的汽车零件将适合.并非所有的汽车零件都是可以互换的......您不能使用门来转向...因此您可以使用零件实现的特定接口.如果他们实现了正确的界面,您的chassee可以放置零件的位置.只要连接螺栓在同一个地方,就可以更换座椅.

2> Patrick Desj..:

编写一个Mock对象并将其用于测试.它们通常非常非常小(继承自抽象类)而不是更多.然后,在单元测试中,您可以调用要测试的抽象方法.

您应该测试包含某些逻辑的抽象类,就像您拥有的所有其他类一样.


@MartiSpamer:我不会说这个答案被高估了,因为它写得比你认为的答案要早得多(2年).让我们鼓励帕特里克,因为在他发布这个答案的时候,这很棒.让我们互相鼓励.干杯
显然可以做到这一点..但是为了真正测试任何派生类,你将反复测试这个基本功能..这将导致你有一个抽象的测试夹具,这样你就可以在测试中分解这个重复.这一切都闻起来!我强烈建议您首先再看看为什么要使用抽象类,看看其他东西是否会更好用.
该死的,我不得不说这是我第一次同意使用模拟的想法.
你需要两个类,一个模拟和测试.mock类只扩展了测试中Abstract类的抽象方法.这些方法可以是no-op,return null等,因为它们不会被测试.测试类仅测试非抽象公共API(即Abstract类实现的接口).对于任何扩展Abstract类的类,您将需要其他测试类,因为未涵盖抽象方法.
过度的下一个答案要好得多.

3> Mnementh..:

我为抽象类和接口做的是:我编写一个测试,它使用对象,因为它是具体的.但是在测试中没有设置X类型的变量(X是抽象类).这个测试类没有添加到测试套件中,而是它的子类,它有一个setup-method,用于将变量设置为X的具体实现.这样我就不会复制测试代码了.如果需要,未使用的测试的子类可以添加更多测试方法.



4> 小智..:

要在抽象类上专门进行单元测试,您应该为测试目的派生它,测试base.method()结果和继承时的预期行为.

您通过调用方法测试方法,因此通过实现它来测试抽象类...



5> Seth Petry-J..:

如果您的抽象类包含具有业务价值的具体功能,那么我通常会通过创建一个用于存根抽象数据的测试双重来直接测试它,或者通过使用模拟框架为我执行此操作.我选择哪一个取决于我是否需要编写抽象方法的特定于测试的实现.

我需要执行此操作的最常见方案是当我使用模板方法模式时,例如当我构建某种可由第三方使用的可扩展框架时.在这种情况下,抽象类定义了我想要测试的算法,因此测试抽象基础比使用特定实现更有意义.

但是,我认为这些测试应该只关注实际业务逻辑具体实现,这一点很重要; 你不应该单元测试抽象类的实现细节,因为你最终会进行脆弱的测试.



6> Ray Tayek..:

一种方法是编写一个与您的抽象类对应的抽象测试用例,然后编写子类您的抽象测试用例的具体测试用例.为原始抽象类的每个具体子类执行此操作(即,您的测试用例层次结构反映了您的类层次结构).请参阅junit recipies book中的测试界面:http://safari.informit.com/9781932394238/ch02lev1sec6 .

还可以在xUnit模式中看到Testcase Superclass:http://xunitpatterns.com/Testcase%20Superclass.html

推荐阅读
php
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有