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

请教下各位大佬 关于单元测试的问题

  •  
  •   www5070504 · 2021-03-10 10:33:21 +08:00 · 5307 次点击
    这是一个创建于 1136 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近遇到几个不知道如何解决或者没有方向的问题 想请教下各位同仁

    一是各位平时写代码的时候会写单元测试么, 或者现在写单元测试的人还多么. 现在为了快速上线, 我这里基本没写过单元测试,各位平时是怎么做的呢.

    二是在单元测试中不知道各位有没有遇到过无法断言的情况, 比如我一个函数会生成一些二进制数据, 我不知道如何断言这个函数产生的结果是合法的数据. 或者比如一个函数产生一些网络数据包, 但是断言的时候没办法进行比较,因为返回值是不容易预测的.

    求各位指教,谢谢各位啦

    67 条回复    2021-03-11 12:50:14 +08:00
    l8g
        1
    l8g  
       2021-03-10 10:42:56 +08:00   ❤️ 2
    一般不会追求覆盖率,就测一下接口通不通,返回值是否符合预期。(其实大部分情况都不写🐶)
    主要还是看公司的规范吧,有的公司是明确要求写且覆盖率有要求的。
    www5070504
        2
    www5070504  
    OP
       2021-03-10 10:50:56 +08:00
    诶 append 找不到了

    请大家畅所欲言吧 不过我估计大家平时是不是都不咋写单测...
    chendy
        3
    chendy  
       2021-03-10 10:52:37 +08:00   ❤️ 1
    java 农,基本上只对“复杂的业务逻辑”,“重要的业务逻辑”和“工具类”写 UT
    接入层不测,数据库和外部接口访问 mock 掉,只测业务逻辑
    meshell
        4
    meshell  
       2021-03-10 10:55:12 +08:00
    只测试通不通🐶
    jaylee4869
        5
    jaylee4869  
       2021-03-10 11:02:34 +08:00   ❤️ 1
    对于第二点,问的太笼统了。。。为啥返回不能预测?难道这个函数使用了系统外部的 API ? mock 一些不确定的输入吧,setup 。
    www5070504
        6
    www5070504  
    OP
       2021-03-10 11:09:23 +08:00
    @jaylee4869 就是比如我现在有一个函数 foo,这个函数会产生一串网络数据包 但是我不知道怎么来进行断言

    比如 assert foo() == value 这里 value 就不不知道该写什么 除非我是把这一串网络数据包都转换成可读文本然后放在 value 的位置

    这个问题我描述起来确实有点笼统, 不知道怎么表达好...
    www5070504
        7
    www5070504  
    OP
       2021-03-10 11:09:51 +08:00
    @meshell 我目前也是, 基本都是自测下通不通 正常调用流程就完事了..
    ruoxie
        8
    ruoxie  
       2021-03-10 11:18:48 +08:00
    我看有些开源项目所谓的单元测试就是测 1===2 , 有什么意义呢
    www5070504
        9
    www5070504  
    OP
       2021-03-10 11:21:15 +08:00
    @ruoxie 不知道啊 但是有时候感觉这玩意在 ci/cd 中能用到, 就自动跑测试啥的
    ayase252
        10
    ayase252  
       2021-03-10 11:30:05 +08:00 via iPhone   ❤️ 1
    就二来说吧,不可能无法预测啊,无法预测说明对函数的职责是不清晰是。如果函数依赖了一些随机因素或者外部因素,把它们 mock 掉就好了
    EKkoGG
        11
    EKkoGG  
       2021-03-10 11:35:10 +08:00   ❤️ 1
    我之前也有同样的疑惑,感觉业务类的有点难做单测,比如一个接口 其实就是数据库的增删改查,mock 掉感觉好像就没测试的必要了
    newmlp
        12
    newmlp  
       2021-03-10 11:37:39 +08:00
    不写,没时间
    Chenamy2017
        13
    Chenamy2017  
       2021-03-10 12:29:57 +08:00   ❤️ 1
    单元测试我感觉是理论上的,编码十几年还真没有看到进行单元测试的,包括某为。
    Mistwave
        14
    Mistwave  
       2021-03-10 12:35:32 +08:00 via iPhone   ❤️ 2
    1. 单测不是测 bug,是帮助你 design
    2. mock 外部依赖;或者将副作用抽出去,专注核心逻辑
    GeruzoniAnsasu
        15
    GeruzoniAnsasu  
       2021-03-10 12:40:13 +08:00
    单测是用来保证设计与实现一致而不是用来保证逻辑正确的

    比如设计上这个函数收到 A 应该返回 B,那单测就给它喂个 A 看能不能收到 B

    “理论上这个函数应该只能收到 A” 于是 试图测试是不是真的只能收到 A 这种测试不在单元测试范畴内。
    GeruzoniAnsasu
        16
    GeruzoniAnsasu  
       2021-03-10 12:43:33 +08:00   ❤️ 1
    @www5070504 对于 #6 提到的场景,如果要做单元测试,那么思路是

    1. 前提:确定 receiver 的正确性
    2. 把 foo 产生的 value 喂给 receiver,在 receiver 上断言行为符合预期
    3. 如果 1 不能确定,递归拆解问题
    supermao
        17
    supermao  
       2021-03-10 12:48:25 +08:00
    单元测试,能测出 bug 来还需要写么
    写的单元测试都是你能想到的并且不会错的
    www5070504
        18
    www5070504  
    OP
       2021-03-10 13:09:21 +08:00
    @GeruzoniAnsasu 多谢 我有点理解了
    dayeye2006199
        19
    dayeye2006199  
       2021-03-10 13:39:04 +08:00
    不写单测一时爽,重构送入火葬场。所以大家的 ci 没有单测,都是运行一些啥逻辑?
    learningman
        20
    learningman  
       2021-03-10 13:43:57 +08:00 via Android
    单元测试拿来入门一些不写或者乱写文档的开源项目挺好用的
    imdong
        21
    imdong  
       2021-03-10 13:46:47 +08:00
    有同样的疑惑:测试一般是分两种吧,一种是单元测试,一种是功能测试。

    我理解的测试应该是避免新增加的改动导致原有的逻辑被改变了而不知道(越改 BUG 越多)。

    但是看到楼上的说法:只喂正确的数据,得到正确的结果。

    意思是对于异常流程,不需要写测试了么?

    比如发布一篇文章:

    对于内容过长或过短,选择的分类或类型错误,时间不正确这类情况等。。。

    求大佬指点,
    iyaozhen
        22
    iyaozhen  
       2021-03-10 14:00:35 +08:00   ❤️ 1
    首先单测是肯定有用的,是利大于弊的

    1. 大公司是有要求的,这么说吧,优秀的项目都是有单测的。当然为了覆盖率一刀切的假单测是没有任何作用的
    2. 前面有人说了。还有一个就是要区分单元测试和集成测试,我见过极端的,全部用集成测试代替单测


    @supermao
    「写的单元测试都是你能想到的并且不会错的」
    写的单元测试都是你能想到 这个没问题,不管什么测试都是这样的。但单测还有个作用就是重构的时候,不管方法内部改动多大,之前能满足的输入输出,重构后也要能满足
    「并且不会错的」这个不对,单测并不是这个方法完全写好后,再写单测,一般是一边写逻辑,一边加单测代码。甚至先把单测代码写好。而且这个方法开发过程中、以及后面的重构,都可能出错,这样单测就能搂住了。有一个共识:发现阶段越早的 bug 越好修复
    www5070504
        23
    www5070504  
    OP
       2021-03-10 14:04:00 +08:00
    @iyaozhen 讲的很清晰 我有点明白了 单测是真正的逻辑 目前的代码和未来重构出来的代码都可以理解为类似单测逻辑的子类
    www5070504
        24
    www5070504  
    OP
       2021-03-10 14:06:31 +08:00
    是这个意思么 自己感觉好像懂了点
    iyaozhen
        25
    iyaozhen  
       2021-03-10 14:07:02 +08:00   ❤️ 1
    @imdong 要把单元测试和功能集成测试区分开

    比如你有一个方法是判断输入参数的,就需要对这个方法进行单测
    「意思是对于异常流程,不需要写测试了么?」异常流程肯定也需要,所以单测里面有个重要的概念:分支覆盖率
    简单来说就是方法里面的各个 if else 是否都覆盖了

    但话说回来,对于单测来说这是可预知的异常分支,比如一些其它异常请求参数非常大,tomcat 就报错了、mysql 网络异常等这些不在单测范畴
    iyaozhen
        26
    iyaozhen  
       2021-03-10 14:09:54 +08:00   ❤️ 1
    @www5070504 嗯嗯 需要多多实践。
    本着减少 bug 、可维护、高质量代码去做就行,单测只是一个手段。为了单测而单测也不行
    CodingNaux
        27
    CodingNaux  
       2021-03-10 14:23:26 +08:00   ❤️ 1
    单元测试不好写,可能是代码结构写的不好
    测试代码要足够简单,断言合理
    mock 掉外部依赖,外部依赖的测试单独测
    盲目追求高覆盖率会很累(前端业务代码 要求 80%的覆盖率,后补单元测试的时候,写的想吐)
    遇到 bug 是要针对补单元测试的

    但,
    突然发现,测试覆盖度和合理的测试断言是两回事,达到覆盖度指标就能过 ci 。。。。。逃
    ahsjs
        28
    ahsjs  
       2021-03-10 14:54:51 +08:00
    会写,已经成习惯了,不过没那么全和细致。
    lzlee
        29
    lzlee  
       2021-03-10 15:02:16 +08:00
    感谢大佬们的分享
    我想问下, 有没有在大厂工作的大佬

    你们的单元测试率, 真的要到 80% 吗
    GeruzoniAnsasu
        30
    GeruzoniAnsasu  
       2021-03-10 15:05:23 +08:00   ❤️ 1
    @imdong 我的说法只是写单元测试的思路 hint 而已,并不是单元测试的方法论……

    当你不知道你写的这坨代码从什么角度写测试时,用我提到的方法来构造一些可递归拆解的 “单元”,拆到你自我感觉足够独立足够单元后,就好对它们进行测试了

    实际上在写的时候,肯定要根据实际情况考虑正负分支、边界异常条件什么的。我是觉得到写 assert 的时候自然会多想到一些条件来 assert 的,不用说太细
    zhuweiyou
        31
    zhuweiyou  
       2021-03-10 15:10:27 +08:00
    不写
    yamasa
        32
    yamasa  
       2021-03-10 15:14:44 +08:00   ❤️ 1
    ut 就是把 mock 的概念吃透,然后勤练就慢慢熟悉了。
    当然是有用的,用 mock 解决掉复杂的外部依赖,专注于每个函数自己的实现正确性,这不重要吗?你能保证你写的复杂业务逻辑每个 corner case 都覆盖了?我甚至遇到好些 corner case 靠自己 e2e 或者集成测试根本没法触发的,这种上了生产就是不定时的雷,ut 在这些时候非常重要。
    idra
        33
    idra  
       2021-03-10 15:17:52 +08:00   ❤️ 1
    作为一个测试,我职业生涯 7 年+以来,看见真正做单元测试的团队只有一个。团队大佬负责写单元测试,然后丢给小弟去实现。个人认为这才是真正教科书上的开发方式:面向单元测试开发。
    yiqiao
        34
    yiqiao  
       2021-03-10 15:27:56 +08:00   ❤️ 1
    敏捷开发,能通就行。剩下的交给客户当黑盒测试了🐶
    moonrailgun
        35
    moonrailgun  
       2021-03-10 15:29:43 +08:00
    会写一些纯函数的单元测试。

    这样就不用通过主动运行人肉调试了
    iyaozhen
        36
    iyaozhen  
       2021-03-10 15:37:44 +08:00
    @lzlee 能水到 80% (逃
    uselessVisitor
        37
    uselessVisitor  
       2021-03-10 15:47:34 +08:00
    看项目的紧急程度
    CitizenR
        38
    CitizenR  
       2021-03-10 15:59:23 +08:00
    @idra 所谓 TDD 是也。好处很多,难度不小。
    www5070504
        39
    www5070504  
    OP
       2021-03-10 16:06:26 +08:00
    @idra 这个流程确实是不错的啊 我刚入行的时候部门经理也是写一些核心的东西然后让我来丰富的 感觉还真挺好的
    www5070504
        40
    www5070504  
    OP
       2021-03-10 16:06:51 +08:00
    @yiqiao 哈哈我现在就在这条路上狂奔
    remarrexxar
        41
    remarrexxar  
       2021-03-10 16:17:17 +08:00
    单元测试覆盖住流程和主要的边界,至少可以保证其他人重构的时候不会失误改坏掉方法,在编译时就可以发现而不是被自动化测试发现再调查。
    orangechengcheng
        42
    orangechengcheng  
       2021-03-10 16:27:16 +08:00
    单测还是挺重要的,反正我们老大一直在要求单测,还有覆盖率 KPI
    dayeye2006199
        43
    dayeye2006199  
       2021-03-10 17:10:44 +08:00
    @lzlee 我前东家的代码是 monorepo,整个公司的人都可以随便改代码。而且你的代码依赖一堆上游,和被下游代码依赖。你得保证上游的更改,不会影响到你代码的功能。所以推荐你的代码至少有 80 的单测覆盖。

    同时你得保证你改了代码,下游依赖你的代码不会被改动弄挂,所以你也希望别人也写多点测试。一来二去,逼着大家把覆盖率搞上去,否则就是找不自在,找加班。
    namelosw
        44
    namelosw  
       2021-03-10 17:11:28 +08:00   ❤️ 2
    > 一是各位平时写代码的时候会写单元测试么, 或者现在写单元测试的人还多么. 现在为了快速上线, 我这里基本没写过单元测试,各位平时是怎么做的呢.

    你可以看看 Ruby TDD 有关的教程之类的, 这种情况只有对开发过程, 不怎么损失开发时间的测试才能坚持写. TDD 做得对的情况是可以比较接近这种状态的 — TDD 的核心是驱动开发, 而不是测试.

    > 二是在单元测试中不知道各位有没有遇到过无法断言的情况

    一个思路是替换或者 Mock, 替换就是依赖注入或者高阶函数, Mock 就是直接 Mock, 除了基础设施之外尽量不要 mock. 就跟随机数一样, 实现里是输入的取真的随机数的实现, 测试里面是可以指定值或者 seed 的实现.

    另外一个思路是分离基础设施和模型(业务代码), 然后主要测中间的模型层. 但是小心不要测的粒度太细, 不然你就会发现要不一直在删测试, 要不代码就不能修改.
    asanelder
        45
    asanelder  
       2021-03-10 17:40:26 +08:00   ❤️ 2
    关于第二点, 俺同意楼上的.

    之所以无法断言, 可能是因为你写代码的时候就不是 TDD 的, 单元测试不是你写完了, 再写测试.

    而是通过写测试再写功能, 如果写功能的时候你发现, 不好测试, 无法断言, 可能就是设计有问题

    这时要修改设计, 想办法让设计能方便的测试.

    这样就是 TDD 的方法论了.

    关于第一点, 待过几个公司, 写测试的寥寥无几, 俺也不知道为什么, 反正俺心里不跑一遍, 就心里没底
    asanelder
        46
    asanelder  
       2021-03-10 17:47:03 +08:00   ❤️ 1
    还有一点要补充, TDD 是一种方法论, 你要多多实践, 不要过于追求什么如何测, 覆盖率, 什么测哪一层, 粒度等等.

    你要知道你测试的目的是什么

    1. 改善设计
    2. 验证功能没问题

    验证功能没问题好说, 那么, 怎么感觉到设计改善了呢?

    比如说, 你慢慢感觉,
    1. 代码中没那么多硬编码了
    2. 每一个组件都可以单独抽出来测有没有问题, 而不是像扯蛋那样, 拿出一个, 要牵连很多
    3. 每一个组件是不是可以方便的换掉啊, 比如你有一个本机的环境, 还有一个测试环境, 很可以很方便在在本机运行, 也可以在测试环境运行, 环境相关的东西可以很方便的替换.

    总之, 就是能感觉到代码的灵活性.

    这就是俺所理解的---<解耦>
    www5070504
        47
    www5070504  
    OP
       2021-03-10 17:54:40 +08:00
    @namelosw
    @asanelder

    多谢两位 感觉很有帮助 确实我是先写的业务代码才写的单元测试 以后知道怎么做了 感谢
    asanelder
        48
    asanelder  
       2021-03-10 17:57:41 +08:00   ❤️ 1
    @www5070504 #48 还有也不是先写测试再写代码, 总之, 俺觉得这不是一个两阶段问题, 也不是一个线性问题, 是一个反复问题, 是一个螺旋上升的问题.

    总之, 要记住目的, <写出更好的代码>

    不要过于纠结于细节.
    balabalaguguji
        49
    balabalaguguji  
       2021-03-10 18:10:19 +08:00
    易文档可以写接口的测试用例,可以方便断言 https://easydoc.top

    截图:![image.png]( https://i.loli.net/2021/03/10/62DESdwNmQlazcK.png)

    还有视频教程 https://www.bilibili.com/video/BV1nh411974p?p=8
    liujialongstar
        50
    liujialongstar  
       2021-03-10 18:20:34 +08:00
    先写业务再写测试, 写单元测试是因为公司要求覆盖率 80%以上
    why1001
        51
    why1001  
       2021-03-10 19:10:31 +08:00
    都不知道 TDD 是啥,我废了
    guyeu
        52
    guyeu  
       2021-03-10 20:17:51 +08:00   ❤️ 2
    函数的功能只要可以描述,就可以测。返回值如果无法预期,说明函数实现得有问题。假如返回值无法预期是因为外部依赖(比如 /dev/random ),那么需要把这个外部依赖 mock 掉,只测试属于你这个函数本身的逻辑。
    liuxu
        53
    liuxu  
       2021-03-10 20:22:49 +08:00
    以前为一个直播项目用 phpunit 写了,全覆盖
    leeg810312
        54
    leeg810312  
       2021-03-10 20:59:04 +08:00 via Android
    测试驱动开发(英语缩写 TDD )以国内的行业习惯应该很难贯彻的。我现在的项目主要都是写 web API,控制器里的代码很少,基本没有单元测试。控制器调用的方法中会选择一些业务逻辑处理占比较高的做单元测试,CRUD 比例高的方法就忽略了,业务逻辑比例高的方法一般都是关键代码,所以需要靠单元测试保障,重构时也容易做回归测试。
    ljf
        55
    ljf  
       2021-03-10 21:09:20 +08:00
    垃圾公司,要求覆盖率 80%,否则通报。写的想吐
    slzcz
        56
    slzcz  
       2021-03-10 23:09:14 +08:00
    正在单元测试的路上.
    一开始追求业务快速完成,回头做单元覆盖感觉就是来了一遍重构。
    ZnBDPang
        57
    ZnBDPang  
       2021-03-10 23:21:15 +08:00
    那当然是,写完自己点来点去
    qoras
        58
    qoras  
       2021-03-10 23:32:25 +08:00
    是否写单元测试, 跟很多因素有关, 比如是否有规范明确要求, 是写业务还是写中间件, 给的工期是否充足, 是否有充足的 qa 配比, 接口是否方便通过外部请求来调用.
    要是项目时间醒来就紧的很, 还每个都写单测, 那还是自找不痛快
    vindurriel
        59
    vindurriel  
       2021-03-11 04:59:35 +08:00 via iPhone
    测试环节能提供另一种视角来审视你的代码 让边界和接口更合理 更便于参与团队合作 之前不理解依赖注入 控制反转 函数式编程的意义 后来写单测明白了 我一般在写单测的同时重构代码
    OHyn
        60
    OHyn  
       2021-03-11 06:04:53 +08:00
    前端瑟瑟发抖。
    放到 npm 上的包做了自动的 E2E 测试。
    封装好的纯 js 工具会写单测。
    组件库会写单测---基于 snapshot 的那种。
    重要业务流程会写自动 E2E 测试。
    明天就上线的什么都不写。
    anonydmer
        61
    anonydmer  
       2021-03-11 09:20:13 +08:00   ❤️ 2
    楼上 #44, #45, #46 的兄弟已经说的很清楚了;单元测试还是应该写的,项目忙,总有不忙的时候,自己慢慢写;如果先是写的代码后来补的测试,通常写单元测试时候你会发现大量的不方便和麻烦的地方;而这正是单元测试一个重要的意义所在,通过单元测试来重构和改善自己的代码;让代码设计更合理,更解耦,更容易测试;写久了之后再写代码时候即使不写测试,也会让你思维有很大的转变,在写之前就会考虑这些代码别人要怎么用,怎么测;这也是 TDD 的意义。

    从我的经验来看,愿意写单元测试和会写单元测试的工程师,最终的代码设计和质量明显会高别人一节;所以我建议,楼主能多写单元测试就多写单元测试,而且就个人的经验来讲,通过写单测去提升代码的设计能力,比去空学各种设计模式和开源项目更有效。
    freeair
        62
    freeair  
       2021-03-11 09:23:44 +08:00   ❤️ 1
    某种意义上,这是个很好的问题。

    高质量要求的项目,单元测试必做的,这也是所有测试过程中,成本很高的一个阶段。而且根据方法论不同,可以分别做白盒测试、黑盒测试。

    但是,单元测试做不做,采取什么样的测试方案等,其实取决于遵循的质量标准以及项目整体计划(预算),不是某个开发人员决定的。

    反过来思考,从单元测试的情况能看出一家公司以及团队的水平。

    第二个问题很多人回复了,就不重复。
    wangyzj
        63
    wangyzj  
       2021-03-11 09:24:29 +08:00
    没事干了就写
    能没事干吗?
    Debiancc
        64
    Debiancc  
       2021-03-11 10:27:26 +08:00
    外企,写。公司强制有 75%+的测试覆盖度要求。
    但是见过骚操作是 为了覆盖率 调了方法 却不写任何断言
    caizs320525
        65
    caizs320525  
       2021-03-11 11:48:40 +08:00
    我理解是项目初始阶段 不写, (毕竟活不活下是个问题
    人员开始扩充了,要立马开始补 ( 不经人员素质参差不齐,不给定几个锚点, 不定被别人改成啥样
    lzlee
        66
    lzlee  
       2021-03-11 12:49:19 +08:00
    @iyaozhen
    谢谢大佬
    lzlee
        67
    lzlee  
       2021-03-11 12:50:14 +08:00
    @dayeye2006199
    谢谢大佬
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   2828 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 14:38 · PVG 22:38 · LAX 07:38 · JFK 10:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.