V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
tikazyq
V2EX  ›  程序员

单元测试的爱恨情仇,作为码农们,你们写单元测试么?

  •  1
     
  •   tikazyq ·
    tikazyq · 2022-10-11 19:01:43 +08:00 · 6603 次点击
    这是一个创建于 758 天前的主题,其中的信息可能已经有所发展或是发生改变。

    作为从事软件开发多年的资深码农,看遍了各种事故和火葬现场。在接触测试驱动开发以及敏捷之后,发现这正是解决 bug 丛生、issue 漫天,最终导致 996 、007 修 bug 的困境。

    这里将单元测试的一些看法分享给大家,希望能够提供一些启发。英译版同步发布在 dev.to,欢迎一起交流学习。

    下面是正文。


    浅谈测试:单元测试的爱恨情仇

    引子

    "开发安全可靠的应用程序的最好方式,就是不写代码。"--Kelsey Hightower

    很多开发者应该或多或少听过单元测试( Unit Tests ),甚至编写过,也或许对其有所了解。不过,在如今瞬息万变的环境下,单元测试似乎正在成为鸡肋。程序员们都知道它的好处,但是对其显得比较冷淡。“进度这么赶,还有什么时间写单元测试呢?”这样的话是不是听着很熟悉?

    单元测试是什么?

    所谓单元测试,简而言之就是程序员编写测试代码来验证自己写的功能代码是否能按照要求运行。如果测试代码不能通过,就说明自己写的功能代码是有问题的。

    这种自己测自己的方式似乎有些可笑,相当于考试时看着答案做题。然而在测试领域,这样的方式有个专业术语叫白盒测试。而白盒测试的对立术语叫黑盒测试,也就是用其他方式来验证。单元测试属于白盒测试,而更高级的测试例如集成测试( Integration Tests )、端到端测试( End to End Tests )、UI 测试( UI Tests ),都属于黑盒测试。单元测试仅仅是测试代码本身。

    Testing Pyramid

    单元测试有什么用?

    单元测试在敏捷开发( Agile Development )中是非常有用的工具。甚至有些敏捷框架,例如极限编程( XP ),就要求每一个功能必须被单元测试覆盖。在之前的文章《浅谈敏捷:你的团队在正确实践敏捷吗?》就提到过单元测试的重要性。

    概括来说,单元测试有下面几个重要作用:

    • 可持续质量保证:单元测试能保证业务逻辑代码在经常性的被更改或重构之后不被破坏
    • 自动化集成:单元测试一般可以集成到 CI/CD 中,当代码提交后自动触发
    • 功能文档化:测试用例可以帮助新接触该代码的开发者熟悉代码的功能和验证条件

    因此,表面上来看,单元测试对于软件开发来说会带来比较大的收益。

    为什么不写单元测试?

    单元测试可以提高我们的产品质量、测试效率,那为什么程序员还是不喜欢写单元测试呢?据 JetBrains 统计,被调查者中仅有 57% 要写单元测试,仅有 35% 会在大部分项目中实施自动化测试。

    那么,为什么?有几个可能的主要原因:

    • 工作量变多:“50 行代码的功能,要我写 100 行来测?不加班能完成?”
    • 专业自信:“我一个资深程序员,写出来的程序怎么可能有 bug 呢?”
    • 感觉不适用:“就这么几个页面,也需要写单元测试?”
    • 有专人测试:“反正都有 QA 了,找他们来不就是找 bug 么?”

    然而,这几个原因都经不起推敲:第一,长期来看自己修复 bug 写的代码量肯定远不止这么点;第二,不管是多么牛逼的程序员,代码写多了,根据大数定理,都会有失误的时候;第三,简单功能如果被引用多了,也会变得重要;第四,QA 的主要工作是保证整体质量,而单元测试是保证局部质量,缺陷部件组装出的产品会优质么?

    变得有远见

    其实,这里最主要的原因是思维模式,程序员大部分时候会站在个体的角度,思考短期的问题,从而忽略了长期的成本收益。作为一个合格的开发者,最主要目标是用最有效的方式开发出最大价值的功能。因此,单元测试在短时间内无法创造价值,从而被很多人忽视。

    单元测试就像读书,短期内不能让人知识渊博、名利兼收,但长期来看是会有效果的。单元测试如果成为习惯或组织文化,就会更容易打造高质量产品和持续交付。要在团队中推广单元测试,需要更根本的流程,例如极限编程、测试驱动编程,或者更高决策者,例如 CTO 、架构师。

    社区

    如果您对笔者的文章感兴趣,可以加笔者微信 tikazyq1 并注明 "码之道",笔者会将你拉入 "码之道" 交流群。

    本篇文章英文版同步发布在 dev.to,技术分享无国界,欢迎大佬们指点。

    79 条回复    2022-10-13 09:42:32 +08:00
    Twnysta
        1
    Twnysta  
       2022-10-11 20:19:42 +08:00
    以前写,但是赶不上业务。然后就都放弃了。
    tikazyq
        2
    tikazyq  
    OP
       2022-10-11 20:57:28 +08:00   ❤️ 1
    @Twnysta 跟健身一样的,如果不努力就坚持不下去了 hhhh
    test4zhou
        3
    test4zhou  
       2022-10-11 20:59:52 +08:00
    通用函数会写
    learningman
        4
    learningman  
       2022-10-11 21:19:38 +08:00
    主要是不好拆,函数本身好测,但是要 mock 可太麻烦了
    SHF
        5
    SHF  
       2022-10-11 22:51:08 +08:00   ❤️ 1
    写 4 年代码到现在,没刻意的写过测试,一般的来说跑起来试一试就能发现问题。在代码中做好 assert 就够了。有写测试代码的时间,不如多想想实现的逻辑,或者在写代码前做好设计。
    error101
        6
    error101  
       2022-10-12 00:38:45 +08:00
    前端怎么写单元测试比较好
    dayeye2006199
        7
    dayeye2006199  
       2022-10-12 05:44:40 +08:00   ❤️ 3
    之前试过 TDD ,发觉在搞增删查改的时候并没有想象中顺手。
    知道我开始写一些框架类代码的时候,才发觉 TDD 的好处。思路非常的自然,先站在用户的角度想别人要怎么使用你的框架,然后写出测试用例,最后去补实现。

    这样写出来的框架,质量既稳健,同时又能站在用户的交付思考很多使用难易度的问题,非常的有帮助。
    knightgao2
        8
    knightgao2  
       2022-10-12 09:01:01 +08:00
    来个前端的 real world 项目,我来学学 TDD 的怎么写
    coldmonkeybit
        9
    coldmonkeybit  
       2022-10-12 09:20:16 +08:00
    借楼请教一个问题,我是完全没有测试经验的小白,在实际业务中想测试一个接口是怎样写的。
    例如一个 post 新增接口,会同时操作几张表,那我每测试一次数据库中就会多几条数据么?
    还是会通过其他手段模拟数据呢
    tikazyq
        10
    tikazyq  
    OP
       2022-10-12 09:49:00 +08:00
    @SHF 随时在代码中断言,其实就是一种单元测试的方式,只是断言把单元测试放在了运行时
    tikazyq
        11
    tikazyq  
    OP
       2022-10-12 09:50:19 +08:00
    @error101 前端单元测试可以聚焦于 js 业务逻辑层面,而非视图样式(这个可以通过 UI 测试完成),特别是通用组件,也可以加单元测试
    lysS
        12
    lysS  
       2022-10-12 09:53:24 +08:00   ❤️ 2
    @coldmonkeybit #9 ut 一般是针对一个函数,你这个太大了。不过非要测也可以,只不过测试环境要数据库,post 完了再读出来看看是否符合期望
    wszzh
        13
    wszzh  
       2022-10-12 09:54:29 +08:00   ❤️ 2
    @coldmonkeybit 用我有限的测试经验回答你,可以建一个测试数据库,使用 flyway 这类软件,测试执行前往测试数据库插入一些测试数据,测试结束清除数据库
    tikazyq
        14
    tikazyq  
    OP
       2022-10-12 09:57:27 +08:00
    @dayeye2006199 是的,经历了社会毒打,才知道来之不易的幸福 hhhhh
    Menkou
        15
    Menkou  
       2022-10-12 10:01:33 +08:00
    @error101 可以试试 cypress 可以写组件测试
    wolfie
        16
    wolfie  
       2022-10-12 10:06:30 +08:00
    不写,没什么好处,而且大多数人 mvn skip test 都不会。

    涉及到升级依赖版本时候,挺有用的。
    unco020511
        17
    unco020511  
       2022-10-12 10:15:16 +08:00
    客户端写起来非常非常麻烦,且收益不大
    tikazyq
        18
    tikazyq  
    OP
       2022-10-12 10:15:40 +08:00
    @knightgao2 可以关注微信公众号“码之道”( codao_blog ),后续会有关于前端单元测试的更新
    tikazyq
        19
    tikazyq  
    OP
       2022-10-12 10:17:24 +08:00   ❤️ 1
    @coldmonkeybit 这涉及到单元测试和更高级一点的接口测试了。就单元测试来说,颗粒度更小,已于操控,如果有依赖的话,可以采用 mock 的方式来解决,因为单元测试只是测试代码本身;接口测试就属于黑盒测试,用 postman 之类的工具就可以了
    rodrick
        20
    rodrick  
       2022-10-12 10:19:23 +08:00
    react hooks 之后就不写单测了 难以突出 unit 的概念
    tikazyq
        21
    tikazyq  
    OP
       2022-10-12 10:19:54 +08:00
    @lysS 可以用 mock 来做
    tikazyq
        22
    tikazyq  
    OP
       2022-10-12 10:20:37 +08:00
    @wszzh 这种方式也可以,虽然有依赖,但也足够简单,也属于单元测试
    tikazyq
        23
    tikazyq  
    OP
       2022-10-12 10:21:22 +08:00
    @Menkou 一般 UI 测试、集成测试用的这类工具,其他还有 playwright 、selenium 、puppeteer
    tikazyq
        24
    tikazyq  
    OP
       2022-10-12 10:22:56 +08:00
    @wolfie java 有 junit 还是挺好用的,写单元测试还是很考验经验的,如果设计的好,整个架构都会良性优化
    wupher
        25
    wupher  
       2022-10-12 10:23:22 +08:00   ❤️ 1
    很有好处,尤其是有问题可以直接回归测试,发版本前自动测试。

    业务越复杂,mock 越麻烦。
    有专门测试数据库,但是数据库表的测试数据维护又是另外一个麻烦和依赖。

    测试让代码可维护可依赖。但有时候测试代码本身又需要维护和依赖。TDD 不是坏事,只是,嗯,要看业务和管理是否愿意承认它的价值和代价了。
    tikazyq
        26
    tikazyq  
    OP
       2022-10-12 10:24:10 +08:00
    @unco020511 客户端没怎么接触过,不知道 ut 是否能有收益
    urnoob
        27
    urnoob  
       2022-10-12 10:24:16 +08:00
    业务开发不要写单元测试!!!!!!!!!!!!!
    tikazyq
        28
    tikazyq  
    OP
       2022-10-12 10:26:42 +08:00
    @rodrick 前端的 ut 确实需要看看大神们如何写的
    tikazyq
        29
    tikazyq  
    OP
       2022-10-12 10:28:40 +08:00
    @wupher 很有道理,大佬应该是 TDD 专家,看起来很有经验。UT 的收益一般在代码覆盖率不饱和的情况下收益很大,越到后面边际效应越明显,所以中庸之道才是正解
    tikazyq
        30
    tikazyq  
    OP
       2022-10-12 10:28:57 +08:00
    @urnoob 有什么故事?😂
    frank1256
        31
    frank1256  
       2022-10-12 10:29:28 +08:00
    项目刚开始开发,不稳定,写单测来不及写就业务变化了。稳定后再补充单测。提交代码时会走单测和自动化测试脚本,过了才可以 review
    tikazyq
        32
    tikazyq  
    OP
       2022-10-12 10:30:40 +08:00
    @frank1256 这也是可以的,跟敏捷类似,没有课本中那种教条的规则,只要适合自己团队和开发模式就可以
    JasonLaw
        33
    JasonLaw  
       2022-10-12 10:38:03 +08:00   ❤️ 1
    @dayeye2006199 #7
    &t=577s - Write Your Tests First!
    zhuweiyou
        34
    zhuweiyou  
       2022-10-12 11:06:00 +08:00
    又不是做开源, 业务开发不写, 测试没写完, 需求又改了.
    james2013
        35
    james2013  
       2022-10-12 11:08:07 +08:00
    不写
    写单元测试是额外的时间,需要加班
    而且需求改了后,是不是又要重新写?
    GeorgeGalway
        36
    GeorgeGalway  
       2022-10-12 11:09:48 +08:00   ❤️ 1
    国内的单元测试会加速内卷
    hsuyeung
        37
    hsuyeung  
       2022-10-12 11:15:09 +08:00
    以前不写,在学习如何写。

    借楼问下,我有一个方法,根据传入参数会有多种不同的错误信息。我应该是为每一种情况编写一个测试方法还是在一个测试方法里把所有情况都写一遍?
    coldmonkeybit
        38
    coldmonkeybit  
       2022-10-12 11:19:36 +08:00
    @tikazyq 十分感谢,很有帮助
    rekulas
        39
    rekulas  
       2022-10-12 11:24:53 +08:00
    基本不写 个别项目偶尔会写-时间很充裕的情况下
    写测试比开发还费时间,更大的问题实际测试异常复杂 单测压根覆盖不了多少情况,性价比极低
    rekulas
        40
    rekulas  
       2022-10-12 11:30:35 +08:00
    我们都是开发完了把所有开发完了人工测试一遍所有可能相关的流程,虽然麻烦但是稳
    yagamil
        41
    yagamil  
       2022-10-12 11:32:06 +08:00
    靠经验 , 自己用 postman 测试
    marcong95
        42
    marcong95  
       2022-10-12 11:42:11 +08:00
    同蹲一个前端单元测试项目开开眼。。。
    litmxs
        43
    litmxs  
       2022-10-12 11:51:42 +08:00 via Android

    要求行覆盖率 80%以上,函数覆盖率 100%
    单元测试写的比较完善的话,修改代码后跑一遍单元测试就能验证改动是否有问题
    anonydmer
        44
    anonydmer  
       2022-10-12 11:55:19 +08:00
    @hsuyeung 这个没有固定的模式,你这两种方法都很常见,选择自己认为合理的好维护的就可以。 比如你这个参数的逻辑要改的时候,可以很方便的先定位到对应的单元测试代码中去即可。 我个人一般喜欢把常规逻辑和边缘性的异常情况分开来成不同的方法写
    saberlong
        45
    saberlong  
       2022-10-12 11:56:17 +08:00 via Android
    写框架,函数库,基础设施时写比较好。业务的话,业务复杂起来后,维护成本很高。比如测试用例的执行顺序变化和并发执行时,会导致新增单测在单跑时正确,全部一起跑时就可能有时正确有时错误,维护非常耗时间。
    hsuyeung
        46
    hsuyeung  
       2022-10-12 12:03:20 +08:00
    @anonydmer 我自己目前也是更倾向于每种情况一个测试方法,这样每个测试方法很容易一眼看懂逻辑。但是这样没法直观地看到有哪些可能的情况(需要去把每个方法都看看)。所以有点纠结了。
    microxiaoxiao
        47
    microxiaoxiao  
       2022-10-12 12:42:35 +08:00 via Android
    三天两头修改就别写了,前两天 BA 让修改个字符串大小写,下划线。自动化测试的姐姐一通抱怨。
    ericgui
        48
    ericgui  
       2022-10-12 12:53:15 +08:00
    今天花了 30 分钟修了一个 bug ,花了 2.5 小时写 test case

    因为一个时间有关的函数,本地机器和服务器机器不在一个时区,这就很傻逼
    jones2000
        49
    jones2000  
       2022-10-12 12:58:22 +08:00
    单元测试还要开发写, 不是测试组写的吗
    GiftedJarvis
        50
    GiftedJarvis  
       2022-10-12 13:03:38 +08:00
    之前的公司单元测试和集成测试都会写, 现在公司的屎山, 写测试太恶心了
    vivipure
        51
    vivipure  
       2022-10-12 13:03:59 +08:00
    前端业务代码不写,组件库和 SDK 开发写
    Rooger
        52
    Rooger  
       2022-10-12 13:25:03 +08:00   ❤️ 5
    - 最近也有想法,写一篇关于自己对单元测试的看法。无奈身体不适,暂时还没有动,所以先在此处写下自己的观点,但愿能帮助一些迷茫的同学。
    - 简单介绍自己的经历,9 年游戏后端,开始用 C++,后面转 Go ,都没有意识到单元测试的重要性,直到去年看了一些博客和课程,才意识到单元测试的重要性。
    - 我的结论是,后端服务单元测试一定要写,而且在时间允许(少摸点鱼)的情况下,一定要尽可能完善。
    - 原因:
    - 正常的逻辑经过通过正常逻辑的单元测试之后,可以避免前端同学人肉验证逻辑的正确性,好处是提高前端同学的对接体验,同时也给提高了后端人员的靠谱属性。
    - 特别节省联调时间,前后端开发节奏并不一致,经过详细单元测试之后的逻辑,前端同学和测试测试极少出问题,并且不用打断后端的节奏,被打断的坏情绪会少很多。
    - 适应频繁修改造成的不稳定性。看到有人抱怨需求频繁变更,就不想写单元测试了。这只是借口而已,正是因为频繁变动,才更需要。避免改动导致大面积逻辑异常真的可以依靠详细的单元测试。
    - 节省后期维护成本,三个月甚至三年之后,你可能需要修改这段代码。如果有完整的单元测试,那绝对是相当舒服的一件事情。
    - 最后补上一句,尊重自己,尊重这个行业。不管是自己维护,还是别人,都用心的去编码。即使是屎山,我也希望自己加进去的不是屎。
    charlie21
        53
    charlie21  
       2022-10-12 13:26:42 +08:00
    开始写一些框架类代码的时候,才发觉 TDD 的好处
    YSMAN
        54
    YSMAN  
       2022-10-12 13:39:11 +08:00
    确实国内的环境 变化太快了 单元测试劳动成本 不可小视
    devHang
        55
    devHang  
       2022-10-12 13:41:20 +08:00   ❤️ 1
    写测试的好处在长期维护一个项目的时候才会体现出来。
    TDD 是一个好的梳理代码逻辑的办法。
    但如果不是自上而下的话,很难推动。
    jsjgjbzhang
        56
    jsjgjbzhang  
       2022-10-12 14:03:43 +08:00
    我想知道前端怎么写单元测试
    NoKey
        57
    NoKey  
       2022-10-12 14:08:59 +08:00
    不写单元测试的原因,其实主要还是很多项目组或者公司不重视,或者,用人力去解决了。

    安排工作量的时候,单元测试根本没有安排,实际工作已经满负荷了。

    说白了,不写单元测试都 996 了,项目组或者公司根本没有考虑单元测试的时间。

    强行加单元测试,就是 997 了
    micean
        58
    micean  
       2022-10-12 14:14:12 +08:00
    月经贴吗
    写 library 肯定要写单元测试,写业务不会写
    aviator
        59
    aviator  
       2022-10-12 14:27:01 +08:00
    做业务的写单测感觉会比写业务代码本身耗时还长
    bingoshe
        60
    bingoshe  
       2022-10-12 15:13:53 +08:00
    1.单元测试时间不够,取决于你的 leader ,你的 leader 当然会跟你说要写,但是不会给你时间
    2.996/007 不是因为事情多,而是要对你精神控制
    leegradyllljjjj
        61
    leegradyllljjjj  
       2022-10-12 15:16:28 +08:00
    想啥呢,国内大部分软件公司都是能跑就行,大部分测试都没有还要程序猿兼职测试,UT 就更别想了
    mazai
        62
    mazai  
       2022-10-12 15:19:10 +08:00
    写,必须写,虽然 100%的覆盖率并不代表代码 100%没 bug ,但是可以为我们程序员增加自信呀,比如重构代码,fix bug 不会出现这个 bug 解决了,却影响到其他 feature 的情况。
    fulvaz
        63
    fulvaz  
       2022-10-12 15:21:34 +08:00
    必须写。 能否单测是衡量代码质量的关键因素。

    定理:如果我的代码没法测试, 肯定是我在写屎
    mazai
        64
    mazai  
       2022-10-12 15:21:39 +08:00
    国内之所以大部分人鄙视单测,第一个原因是不会写单测,第二个原因就是对软件工程文化不够重视。
    exonuclease
        65
    exonuclease  
       2022-10-12 15:28:11 +08:00
    反正在外企大部分情况下可以测试的代码都需要被覆盖 有一些比较奇怪和 hack 的代码没有 比如魔改 appdomain 的。。。
    tikazyq
        66
    tikazyq  
    OP
       2022-10-12 15:32:06 +08:00
    @Rooger 大佬说得对,给大佬递茶🍵
    PainAndLove
        67
    PainAndLove  
       2022-10-12 15:40:34 +08:00
    收藏一下,等有结论了再看
    newmlp
        68
    newmlp  
       2022-10-12 15:40:46 +08:00
    给钱就写,不给钱还写个毛,不过大部分公司都不给钱
    pigf
        69
    pigf  
       2022-10-12 16:14:04 +08:00
    会写 http client 请求
    franklinray
        70
    franklinray  
       2022-10-12 16:26:08 +08:00
    动不动就是让你 1 、2 个月赶紧做个 demo 上线演示。演示完再给 1 、2 个月完善成产品。代码都写不完,还单元测试。别一说这个问题就是国内没有软件工程文化之类的。如果公司允许我每周就产出一个小函数。我当然愿意单元测试完全覆盖好。说到底还是资本家需要你尽快把代码变现。质量问题就让 QA 操心好了。
    linvaux
        71
    linvaux  
       2022-10-12 16:42:27 +08:00
    写 ut 的速度赶不上需求变更的速度,写个毛
    erlking
        72
    erlking  
       2022-10-12 16:52:51 +08:00
    通常会把测试时间估进工作量,但如果被恶意压缩工期,那写测试的时间就变成了 buffer……
    wr410
        73
    wr410  
       2022-10-12 17:39:51 +08:00
    能不写就不写,因为根本没时间,扔给测试测去有 bug 再说,反正他们测试案例想的比我周到
    shenyangno1
        74
    shenyangno1  
       2022-10-12 18:00:52 +08:00
    测试中真实调用其他系统的接口、中间件交互(数据库等),算单元测试还是集成测试?一直很迷茫单元测试和集成测试的界限,不同公司的标准好像不太一样,以前写单元测试,涉及跨系统 /中间件的交互,都是 mock 。
    nqlair
        75
    nqlair  
       2022-10-12 18:24:01 +08:00
    写,强制要求,没 90%覆盖不给发布
    zjyl1994
        76
    zjyl1994  
       2022-10-12 22:47:59 +08:00
    写过单元测试,然后开发流程特别敏捷(天天改功能),单元测试跟不上进度逐渐就荒废了。现在真运行起来 20%通过率都没有
    yedanten
        77
    yedanten  
       2022-10-13 08:03:39 +08:00 via Android
    有要求就写 没要求就不写,尤其是敏捷开发模式,今天开发明天上线后天改需求发 v2 版本,这业务都来不及搞定,还写什么测试
    forbreak
        78
    forbreak  
       2022-10-13 09:31:01 +08:00
    只有特定的一些开发需要写。写增删改查,业务代码的时候写单元测试,就是写了个寂寞。 今天写完单测,明天需求变了。
    helloworld1024
        79
    helloworld1024  
       2022-10-13 09:42:32 +08:00
    天天 curd ,不需要单元测试
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1028 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 22:03 · PVG 06:03 · LAX 14:03 · JFK 17:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.