谈谈 unit test?

2021-01-28 10:11:36 +08:00
 sockpuppet9527

书接上文, 谈谈 code review: /t/693941

  • 优点:保证工程质量。API 级别的 unit test 。
  • 缺点:过量的 UT 只会导致 API 不好改动,浪费更多时间前期开发+后期维护

最近我的波兰“好兄弟”脑壳一抽,提出要加 UT,早不写,晚不写,非得 API 全都弄完了再写。本来就是谁写代码,谁就应该加 UT 的事情,结果还得先让我提一个 UT plan 上去。

然后我就改了七八版本吧。他有如下要求

写 UT 从来都是好事情,波兰“好东西”就是脑壳不转弯的,现在每天下午快下班了在那吵架,搞得大家都很烦,这个 UT plan 提了几周还在改,我真是佛了。

4031 次点击
所在节点    程序员
38 条回复
petercui
2021-01-28 11:25:36 +08:00
第一点我站“好兄弟”那边;第二个点我站你这边,第三个点我觉得 UT 本来就应该三倍于真正的业务代码,你这才“有的一比”,简直太少了。
guyeu
2021-01-28 11:41:18 +08:00
教条主义单元测试实践
xylophone21
2021-01-28 11:50:23 +08:00
同求这种 C/C++项目的 UT 实践,除了楼主说的打桩问题,makefile 问题不知道你们怎么解决的?比如你第二条里说的 A.c 在 makefile 里本来是依赖 B.o 或者 B.lib 的,但既然要 UT,那就只能依赖 MockB.o,甚至 MockB1.o,MockB2.o 等等(因为要 Mock 不同返回的情况),既然有了 MockB.o,那和 B.o 肯定就不能打包到一个 main 里了(除非全打成动态库,但动态库又有动态库的问题),于是又是一堆 main,一堆初始化……

以前我一直以为 UT 是三倍业务代码就是这些原因,直到我看了 Spring 里的 UT 框架,各种 Repository 、Service 、网络层的 Mock,运行时像注入哪个注入哪个。不想打桩时直接用真实实例也可以直接用(比如测 Service 时,Repository 不想打桩的话,可以直接调用真实的 Repository 准备各种数据——PS,我不知道这种方法是不是不够 UT,但确实好用,节省时间)。所以如果三倍工作量是 Spring 说出来的话,我简直怀疑在 C/C++界,这个工作量可能是 10 倍,30 倍?
sockpuppet9527
2021-01-28 13:00:36 +08:00
@xylophone21 #3

C 的话,有两个办法,其一就是 MockB.o 的办法(正如你自己说的),第二个办法是替换掉 weak symbols,这个办法需要在 Makefile 中显示声明,并且可以让原有的.o 文件被 link 。

C++的话简单很多,如果你用 GTest,里面有 GMock,这是基于模板的,如果你想用 GMock 去替换掉 global 的函数,就得自己加点东西。
sockpuppet9527
2021-01-28 13:03:29 +08:00
@petercui #1 三倍的话,我其实很好奇你是如何做到后期维护的,假如你动了某个方法,那可能你需要改很多 UT 。并且你在改了 UT 的同时,有可能和原先 UT 的设计者想法背道而驰。
feather12315
2021-01-28 13:08:12 +08:00
动态注入,x86 用 call ( 0xe8 )、jmp ( 0xe9 )实现
feather12315
2021-01-28 13:09:02 +08:00
…当前就在写测试代码,利用内核实现的 livepatch 做注入
AndyAO
2021-01-28 13:09:45 +08:00
关于 UT 阻碍修改的问题.
在进行大范围的更改的时候,就应该舍去.
因为测试的很多都是实现细节,而不是行为本身实现,细节本来就可以改的.
这个问题自从 UT 诞生以来就有了,上世纪 90 年代还有人这么说.
要再写单元测试的同时也要写行为测试,当然有必要的话还可以写更高层的.
这样的话,如果更改已经明显越过测试了,那就应该舍弃.

而至于万一测试没问题,但上线之后有问题该怎么办,这个也是老问题了,关于测试的书里很多都谈到了.
有点类似于科学实验的意思,发个火箭造个飞机什么的,也都不可能完全按照现实情况来进行测试内容测试就不能叫测试.

至于论证的话比较偏哲学所以在这里我也就不写了,相关的书里都有.

我写测试也不太专业,我也在补课,感觉很多书都讲的不错,书比较老,也有很多的养分,如果不吸取前人的经验,那么就会重复的方案,90 年代大家都遇到过的错误.
AndyAO
2021-01-28 13:11:52 +08:00
@AndyAO #8 更改上面一段,删除掉不小心打错了部分

而至于万一测试没问题,但上线之后有问题该怎么办,这个也是老问题了,关于测试的书里很多都谈到了.
有点类似于科学实验,发火箭造飞机都不是按照实际情况来进行测试的,也都不可能完全按照现实情况来进行测试.
niubee1
2021-01-28 13:13:25 +08:00
其实很多人一开始都觉得单元测试浪费时间,但是很多时候回过头来看看,没有单元测试才是真把时间都喂狗了
AndyAO
2021-01-28 13:17:17 +08:00
@niubee1 #10
这不就是测试出动开发吗?目前研究表明这个方法还是能够提高效率和代码质量的,至少不会变差.[1]
软件业目前就是这样,很多很多上世纪都出现了概念,但很多程序员却根本就不知道,有时候让人感觉挺悲哀的.[2]
多读书怎么强调也不为过.

[1] AndyOram, GregWilson. 软件之道[M]. 人民邮电出版社, 2012.
[2] Barr A . The Problem with Software[C]// 2018.
sockpuppet9527
2021-01-28 13:20:41 +08:00
@AndyAO #9 那问题来了,现在项目明明可以用实际环境来测试,为什么不用呢?还必须要 Mock 住环境,这不是多次一举?

“发火箭造飞机”这个例子,我的确认可,但拿来和我目前的情况类比,并不合适。

举个最实际的例子,我的依赖库中有大页管理的实现,那现在我在一台测试机上,是可以用到大页的,那我觉得就不需要用 Mock 去模拟,就很多此一举。
AndyAO
2021-01-28 13:29:16 +08:00
@sockpuppet9527 #12

这真是个常识问题,你可能不太了解,但是相关的资料应该不难找,关于测试的书基本上首先都会讲这个问题.

我有这样的建议,你先把使用 Mock 的好处找到然后给列出来,因为 Mock 是几乎所有单元测试框架中都要用的,可以说那是必备的东西.

我估计谷歌上的资料已经很多很多了,因为我看到的书首先都要讲这个.

当然你们在实践的过程中可能遇到的问题让你感觉特别的无奈,不想写这个东西,然后你对这个可恶的东西有了很强烈的情绪.

这个是人之常情,每个人都会这样,但你想一下 Mock,广泛存在于所有单元测试框架中,这个现实就知道,比起这个东西完全没用来说,更大的可能是用错了,有句话说垃圾是放错的资源,何况这个东西显然不是垃圾.

等你把好处列出来之后 "现在项目明明可以用实际环境来测试,为什么不用呢?还必须要 Mock 住环境,这不是多次一举?"这个问题自然而然就回答了,那么你也就知道了,Mock 真正要解决的问题,如果某些问题的确不适合它来解决,那么你也就不用再费心了.

我也把之前看到的资料稍微整理整理发到这上面来供你参考,也许会有点帮助我也复习复习.
AndyAO
2021-01-28 13:33:19 +08:00
@sockpuppet9527 #12
还有我提到的那个行为测试和单元测试的区别,这个的专业术语应该叫测试的颗粒度问题.
当然软件工程的数据比较混乱,相同概念经常有各种各样的词,但我觉得这个好像还是比较普遍的吧.
我是在写行为测试的时候看到过关于这些问题的表述.
我现在都是行为测试和单元测试都要写.
sockpuppet9527
2021-01-28 13:38:25 +08:00
@AndyAO

#14 和行为测试有什么关系,我这里谈的是单元测试。

#13 说实话,看了两遍我都没明白你到底想说什么。我再举个简单的例子,你 alloc 一块内存需要用 malloc,难道你在 UT 里面也要把 malloc 这个函数给 Mock 掉吗?
AndyAO
2021-01-28 13:41:42 +08:00
@sockpuppet9527 #15
说行为测试是为了解释你关于单元测试的问题.
因为单元测试在有些修改的时候是需要舍弃的,你说的那种阻碍了修改的事情,本不应该由单元测试来承担.所以根本就是用错了地方.
如果你这都看不懂的话,那我就不说了,你自己多看点书.资料我也不给你发了.
hitmanx
2021-01-28 13:49:15 +08:00
所以看下来自己给自己代码加测试是比较合适的,哪一些是黑盒的功能测试能覆盖到的,哪些需要白盒测试的,哪些需要 mock,写代码的人应该自己心里最有数,甚至哪些功能是不完整的,哪部分代码将来未来是要扩充的。这些可能都对测试的设计会有影响。我也觉得教条地 mock 每一个函数或者让每一行代码都测试到,进而大量的高度耦合然而并没有社么用的测试,其实只会浪费更多的时间
sockpuppet9527
2021-01-28 13:49:26 +08:00
@AndyAO #16
在#15 提的是#13 的问题,你并没有任何正面回答的意思。你“发不发资料”和你“有没有正面讨论”并无关系。

然后关于你#8 和#14 中的“单元测试在有些修改的时候是需要舍弃”的逻辑,我个人的经验是知识因项目而异。
sockpuppet9527
2021-01-28 13:50:24 +08:00
@sockpuppet9527 #18

纠正,“我个人的经验是知识因项目而异” -> “我个人的经验是因项目而异”
sockpuppet9527
2021-01-28 13:52:43 +08:00
@hitmanx #17 很认同您的观点,不同设计者总是会有不同的想法。 :)

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/749125

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX