设为首页 加入收藏

TOP

TDD学习笔记【三】---是否需针对非public方法进行测试?(一)
2019-09-03 00:01:41 】 浏览:30
Tags:TDD 学习 笔记 --- 是否 针对 public 方法 进行 测试

前言

在Visual Studio 2012 中,针对Unit Test 的部分,有一个重要的变动:

原本针对「测试对象非public 的部分」,开发人员可通过Visual Studio 2010 自动产生的accessor ??来进行测试。但在Visual Studio 2012 中,将此功能移除了。

Accessor ??其背后的原理,是将对象通过很「脏」的反射方式,把对象内所有的东西public 出来。并且Visual Studio 在更新对象后,进行与设计测试时,会帮你做同步产生accessor ??的动作。(实际的原理我没有深入研究,也不太确定。但基本上的概念就是如此)

这个原本被认为很方便、实用的功能(包括我很久之前写测试时,也是这么认为),很抱歉,在Visual Studio 2012 后已经被移除了。

接下来本篇文章将会说明,单元测试是否应该对测试对象非public 的部份,进行单元测试。

单元测试的意义

一言以蔽之:「单元测试就是用来模拟外部如何使用测试目标对象,验证其行为是否符合预期」。

因此,有个重点是:外部如何使用测试目标对象。

让我们回到Object-Oriented 的封装原则,封装的用意在于:

  1. 隔离出对象的内部与外部。也就是定义「对象的边界」,以及定义「外部可视部分」。
  2. 将外部使用端,不需要了解对象的内部信息,封装起来。也就是「封装细节」。
  3. 将对象内部的变化,封装起来。也就是「封装变化」。

有了对单元测试与封装的认知后,接下来说明,为什么单元测试只需要针对测试目标对象public 的行为,进行测试即可。

 

只测试Public 行为?

根据单元测试的意义,以及封装的用意,代表着「外部使用者原本就不需要了解,也根本不了解,测试目标对象非public的行为」。单元测试既然是模拟外部使用端的动作,那当然只针对测试目标对象public的行为进行模拟与验证。

但一些朋友肯定有些疑惑,那非public 的method 该怎么办?不测吗?那code coverage 怎么提升?要怎么知道这些非public 的行为有没如同预期般运作呢?

有这些疑问是正常的,因为我一开始也是有一模一样的疑问,但开始接触TDD 之后,反而更加了解了Unit Test 的本质。

所谓的非public 的行为,其存在的原因,一定是因为某一些public 的行为会用到这些private 或protected 的method,如果对象中存在着跟public method 无关的private 或protected method,那在设计上就是个问题,这些非public 的method 根本就没有存在的意义。因为外部使用测试目标对象时,完全不会用到这些method,就像声明了变量却不去使用它一样,没有意义。

而当私有或受保护的方法与public 方法有关时,那针对公有方法的单元测试便会涵盖到这些私有或受保护的方法,它们就是公有方法的一部分,对外部使用者来说,根本分辨不出来什么是私或受保护的,因为只关注在对象外部可视行为上。

所以,在实作单元测试上,倘若测试对象一个public method 中,涵盖了一个private method,而private method 中与外部对象或服务相依,那么在测这个public method 时,要连private method 中相依的interface ,都要撰写stub object 来模拟才行,这也是为什么单元测试被称为白箱测试的原因。但还是得强调一次,外部使用者是无法分清楚哪一部分是public method 内容,哪一部分是非public method。

总结上面的说法,非public method 的测试涵盖率??,是依据public method 调用时的input 来决定。

有没有可能,当public method 该测的都测了,甚至public method 主体内容涵盖率都100% 了,非public 的部分涵盖率却很低?当然有可能,但这要厘清一下,没有被涵盖到的部份,是属于什么样的代码。

如果在非public method 中,没被测试覆盖的部份,是提醒、断言之类的代码,那么是属于正常的情况。因为可能在调用非public method 之前,就已经先提醒了,导致非public method 中的提醒永远不会发生。但,因为系统的健壮性考量,该断言、提醒、验证的部份,还是不能少。因为不会知道未来其他方法调用前,有没做好提醒的部份。

那么,在private或protected method中,非提醒、断言的代码,却又没被涵盖到部分呢?这是个警讯,代表着这些代码可能是over design,或是根本没有用处。因为这个对象所有对外的行为,所有的可能性,都模拟过一次了,却都不会用到这些没被涵盖到的代码,这不就代表「这些代码目前用不到」吗?YAGNI原则就是在说这件事:「You ain't gonna need it !

只要public 的行为如同预期,即使private 或protected 的method 是hard-code,是很没弹性,是很愚蠢的写法,对外部使用来说,根本就不在乎,因为无感。

这也是TDD 所提倡的精神,如果所有使用行为都符合预期,就代表功能完成了。而且依据测试来撰写的生产代码,几乎不会出现测试涵盖不到的code,因为生产代码 是为了满足测试而撰写的。不需要存在用不到的生产代码,因此,也可以避免over design 的情况。

 

针对非public 行为测试又如何?

上面那一段的说明,肯定还是无法说服所有人,「为什么要把已经存在的功能移除?」

不用accessor ??的人大可不用,但已经在用,或真的得用的人,还是希望可以在VS2012 中继续使用。

回到封装的用意上,「封装变化」一直是对象导向设计中很重要的设计原则。那些针对private与protected进行单元测试的朋友,有没有过「因为一些需求更新,导致单元测试程式就需要跟着重新调整、设计或修改,而且频率与范围导致测试的维护成本增加不少」的经验。如果有,这就是为什么不希望developer去针对非public method写单元测试的原因。

着重在非public method 的单元测试,说穿了只是写给developer 爽而已。因为要封装变化,才会把这些内容变成private 或protected,以期望变化时对外部使用者来说,呈现无感,也就是降低耦合,也就是最小知识原则。

现在单元测试却通过某些机制,来存取这些封装起来的行为,不是自讨苦吃吗?原本就知道,这些东西很可能会一直变化,却又去存取它,测试它,导致单元测试因此维护与更新频率增加,这不就违背了封装的用意?

对使用对象的角度来说,使用端根本不关心这些变化,却因为单元测试用脏方法硬干到这些不公开的行为,导致测试成本增加,进而导致一些不明就里的developer喊出「测试很花成本,时间增加很多,很难维护」。我只想说:「这不是南北拳的问题,是你的问题。」

 

结论

说真的,刚知道Visual Studio 2012 把accessor ??功能拿掉,我也一整个相当吃惊,觉得要强迫developer 用TDD 方式开发,也不用做到这么绝吧。

但将对象导向的原则、TDD 的精神、单元测试的基本意义结合起来后,有了上述的思考历程,就觉得只测试public method,不建议测试private 与pro

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇坑人的运算符 下一篇使用LINQ 對List分頁/區

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目