Soft_Job 板


LINE

因为大家的讨论都很基於心法 实作上相对很模糊 利用这个机会也跟大家请教实作上的方式 因为最近工作被指派要针对公司产品的程式做整理,其实运作都还好 只是大家功能是一层叠一层,一堆巢状逻辑,跟依赖中的依赖 也没有任何的测试跟注解,当然也没有任何测试 举个例子 有个功能是储值,可以接受银行、信用卡、或是一个临时帐号 然後储值前需要身分验证,然後银行的话要检查是不是有绑定 总之每个方式都有一些共用,或是非共用的行为 目前的程式像这样 func 储值(方式){ switch 方式{ case 方式1: if 符合条件1 { if 符合条件2 { if 呼叫api-1 成功{ 更新介面1 } } } case 方式2: 要符合不同的巢状条件,然後呼叫另一只api,一样根据结果更新不同的介面 case 方式3: 又是不同的条件跟api } } 像这样的程式,不知道测试怎麽写? 单看这个函式的cyclomatic complexity,大概是20几吧 如果把测试写成涵盖所有的可能 像是=> 当选择方式1储值,且不符合条件1,就要如何如何 整个测试就写不完了 而且条件本身又依赖於其他的api或是系统参数 就算写完了测试,涵盖率虽然高,但我觉得是没有解决核心的问题 我就只是要确认我程式本身的逻辑没问题,不需要涵盖别人的东西 这边我实在很好奇大家的作法是怎样? 是跳过不做,还是真的就写一个涵盖所有可能的测试 我的选择是先重构啦 先定义一个抽象的RechargeCommand介面 包含执行储值操作所需的方法 其中execute()会统一回传一个Result物件,代表储值结果 接下来为每种储值方式实作具体的命令 例如BankRechargeCommand、CreditCardRechargeCommand和AutoRechargeCommand等 然後一些通用的行为,像是登入验证、密码输入等,用装饰者模式 定义一个RechargeCommandDecorator抽象类别,继承自RechargeCommand介面 最後为每种需要共用的行为创建一个具体的装饰者,继承自RechargeCommandDecorator 例如 LoginCheckDecorator和BioAuthDecorator 呼叫的时候,根据需要去组合不同的储值方式,跟需要的共同行为 回传值也都是相同的Result物件 呼叫会像这样(我是ios swift) let bankRechargeCommand: RechargeCommand = BankRechargeCommand() let loginCheckDecorator = LoginCheckDecorator(bankRechargeCommand) let bioAuthDecorator = BioAuthDecorator(loginCheckDecorator) let result = bioAuthDecorator.execute() 呼叫api的部分用策略模式做依赖注入 在command实作中,接收一个RechargeAPIService,预设是真实呼叫 做单元测试的时候就单独传入mock service 测试也改成针对不同的储值方式做测试 而不是去涵盖所有的可能路径 这样就可以大幅减少测试案例的撰写,也可以节省复制贴上的时间 我只要确定我的内部逻辑正确,理论上不管怎样的路径应该都不会出错 也不会被绑定在外部依赖 整个流程大概就是把一个巢状判断函式 更改为 command+decorator 模式,根据需要呼叫与包装对应的行为 然後外部的依赖透过注入,在测试时使用mock避免被干扰 也请大家看一下 不知道这样的修改是不是可以 还是应该以涵盖所有的可能状况为优先 @@? ※ 引述《TonyQ (得理饶人)》之铭言: : 先说我不是故意要回两篇, : 但刚看到 landlord (就 joey chen, 江湖名 91) 在 FB 的回应,我觉得也蛮好的, : 他说他最近在忙没空过来,我问过他之後帮他转过来。 : 以下基本上逐字照转 : (source from https://tinyurl.com/rxyerfyw ) : 其实讲到底根本原因反而是跟产品程式码的设计能力有关, : 产品程式设计得越好,测试程式越容易写,越好测。 : 真正需要在测试中做假模拟(隔离)的部分, : 属於外部(拥有权不在我们手上的部分), : 例如外部系统的服务(走通讯协定出去,且不属於我们可以维护跟上版的服务)、 : 三方(package/SDK)。而 DB, redis之类的 cache 甚至是不需要特别被隔离开的。 : 这是由於现代科技的便利,让我们有机会把越来越接近端到端测试的一类, : 比例逐步拉高的可行性比过去容易得多。 : 另一个重点则是当设计越来越偏向高内聚,simple design, : 把 code smell 消除到最後回很自然地提炼出 domain model, : 有了 domain model, : 最复杂的 domain logic 处理一堆散落资料的逻辑都被内聚到model里面, : 没有 application 层的依赖,model 的单元测试也很好写。 : 结论: : 1. 要有能力在 legacy 上重构出可测试性 : 2. 要有能力做出稳定的端到端测试 : 3. 要能精炼设计,将散落的资料内聚在一起 : 来代表 domain 的概念提供 domain 的行为, : 因为设计上本来就没有外层的依赖,model的方法也都精简短小,甚至鲜少回传值, : 自然 API 易用性跟测试都可以比过去万恶的三层式架构+内嵌无限层依赖注入 : 的手风琴架构来得简单跟好测许多。 : 现在大部分的依赖(注入)都不是本质上需要的,而是被开发人员硬生生切得支离破碎的。 : 补充一下 TonyQ 内文最後一点: : 「如果都没被报 bug,你也没有修改它的需要时,帮它加测试干嘛?」 : 这超级重要的,这种情况下加测试往往适得其反, : 只会建立伪阳性/阴性的测试结果,劳民伤财还造成干扰。 : ※ 引述《TonyQ (得理饶人)》之铭言: : : 底下这是比较「野性」」的作法,算是实务专案的经验: : : 其实我觉得你到一个完全没有测试的专案,要分两个策略: : : 1. 补重要主线的 integration test 反正哪边常被报修就补哪边。 : : 如果一开始补不上去就先做下一点,理论上常被报修的地方会一直出现在下一点, : : 累积多了就可以变成1了。 : : 2. 假设自己是维护历史功能,提昇自己维护部分的可测试性。 : : 举实际案例好了,假设我今天再做一个算金流手续费的专案, : : 发现过去算手续费假设有11个地方写了11次好了,所谓的高耦合不外乎如此。 : : 我会先写个 util 把输入跟输出「去状态化」,然後针对这个 util 写, : : 然後这个 util 的单位以「去状态化」成本可控,可在手边开发时间允许的范围进行。 : : 白话文:我横竖都得手动测试,那就把手动测试的部分, : : 尽可能的透过 test code 来进行。 : : 如果不想接着维护的话也很单纯,任务结束後把 test code comment 掉或移除就行。 : : 题外话,11个地方,我会选择先取代一个地方, : : 然後等其他10个地方有需求变更时,一个个整并,补强测试条件。 : : 很多人会说,那为什麽不一次改11个,理由是你的开发时间跟成本允不允许。 : : 更重要的是你的QA资源够不够,因为写正常的Test最累的是准备测试情境, : : 这真的是会花掉比写test更多的时间。 : : 如果列不出完整场景,贸然修改既有的code只是在裸奔。 : : 有需求的部分是被迫裸奔,但没需求的部分不用主动当暴露狂, : : 等待验证过再慢慢统一。 : : 大原则就是:你横竖都是要测试的,只是手测还是写程式测,除了跟 gui 有关的东西, : : 多数的情况下写程式测试都还是比较省时间的。 : : 更棒的地方是,在这种策略下,你往往可以用比同事少30% 的时间完成任务, : : 而且因为你的测试成本比较低,所以品质也会比较好,出问题的时候也更容易对焦。 : : 然後我通常是会跟同事说我写了几个 test case, : : 他们愿意看就看,不愿意看我就放着。不会勉强他们加入。 : : 如果你做不到可以用比不写测试更短的时间来完成任务, : : 那你学的测试根本性的就有问题,不写也罢。XD : : 3. 极端情况: 如果都没被报bug,需求也都很小? : : 那你操心他干嘛 XD --



※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 59.120.34.91 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/Soft_Job/M.1714634857.A.75E.html
1F:→ landlord: 刚好之前写过类似的题目,有带到重构的过程跟影片 05/02 15:35
2F:→ landlord: https://bit.ly/4b0B3Nb 05/02 15:36
3F:→ landlord: 当时也有朋友支援各语言版本库,给大家参考一下 05/02 15:36
4F:→ landlord: 当然,写测试的话还是得知道有哪些情境,才能用测试描述 05/02 15:37
5F:→ landlord: 但这个重构的过程,即使没测试,有pair跟 code review 05/02 15:37
6F:→ landlord: 也不会有太大的成本跟风险问题 05/02 15:37
神...Orz
7F:→ brucetu: 你的最後一段假设就是在说如果单元测试都没问题整合测试 05/02 15:39
8F:→ brucetu: 跟上线理论上都不会爆掉 05/02 15:39
9F:→ brucetu: 实际上呢?如果这个理论正确那就不用写整合测试了 05/02 15:40
10F:→ brucetu: 所以要保证不爆掉当然需要所有的输入变数的可能性跟路径 05/02 15:41
11F:→ brucetu: 都测试过,符合预期,才有可能保证上线不爆掉 05/02 15:41
其实因为我的是app,所以其实我这段测试其实更偏向整合测试而不是单元测试 只是也很好奇 所谓的整合测试,在牵涉到外部的时候,需要一起测吗? 因为我的测试程式本身就包含了登入检查、生物辨识、呼叫api 只是这些部份我使用的是mock 这些物件的测试应该用其他的方式或包装,不应该影响储值这件事跟储值的测试 加上我回传的是一个通用的Result物件 这个Result要不是成功(包含成功储值的相关资讯),要不是失败(失败的相关资讯) 流程中如果任何一个环节失败了,对呼叫端来说都是收到失败的Result 这时候就是处理失败的介面与逻辑 原则上我还是会在测试中补上测失败的测试案例 但应该就是几个常见的,不会去涵盖每一个 我的目标还是很简单,我要确认的是程式这边的逻辑有没有问题 输入变数已经被command模式限制了,以这个案例来说,好像不知道还有啥变数 且不管哪个环节错,他就是抛Result<Error> 额外的测试,说真的,我觉得自己按一下比较快 理论上也不应该出现没捕捉到的错误 真的有...大概也是很稀少的案例,这个就交给内部测试或是实际使用者吧 冏rz ※ 编辑: langrisser19 (59.120.34.91 台湾), 05/02/2024 15:59:22
12F:→ TonyQ: "如果任何一个环节失败了,对呼叫端来说都是收到失败的" 05/02 16:00
13F:→ TonyQ: 这个可能 app 比较没感觉,但是监别不同的失败对debug重要 05/02 16:01
14F:→ TonyQ: 题外话你这个情境算是很特定的情境了,我自己在这个时候会 05/02 16:35
15F:→ TonyQ: 思考的问题是,「假设我的程式出错了,我能不能避开最大的 05/02 16:35
16F:→ TonyQ: 伤害。」 这也是一个可以考虑的事情。 05/02 16:36
17F:→ brucetu: 理论上牵涉到第三方服务的时候你要mock第三方服务 05/02 19:48
18F:→ brucetu: 实务上第三方有提供测试区的话我会直接开一个测试区db直 05/02 19:49
19F:→ brucetu: 接在上面测试 05/02 19:49
20F:推 wulouise: 写一个新的加测试 05/02 21:30
21F:推 OldTjikko: 巢状太多了,重构看看能不能guard clauses 05/03 00:21
22F:→ yamagishi: 太巢惹, ealry return 用一下该测的东西分一下你自然知 05/03 08:32
23F:→ yamagishi: 道 test 要怎麽写 05/03 08:32
24F:→ new122851: 当你发现UT写不下去时,你要的是重构 05/03 19:56
25F:→ rogerlarger: 把每个if内的code都分别做成function内,然後再写un 05/10 14:08
26F:→ rogerlarger: ittest? 05/10 14:08







like.gif 您可能会有兴趣的文章
icon.png[问题/行为] 猫晚上进房间会不会有憋尿问题
icon.pngRe: [闲聊] 选了错误的女孩成为魔法少女 XDDDDDDDDDD
icon.png[正妹] 瑞典 一张
icon.png[心得] EMS高领长版毛衣.墨小楼MC1002
icon.png[分享] 丹龙隔热纸GE55+33+22
icon.png[问题] 清洗洗衣机
icon.png[寻物] 窗台下的空间
icon.png[闲聊] 双极の女神1 木魔爵
icon.png[售车] 新竹 1997 march 1297cc 白色 四门
icon.png[讨论] 能从照片感受到摄影者心情吗
icon.png[狂贺] 贺贺贺贺 贺!岛村卯月!总选举NO.1
icon.png[难过] 羡慕白皮肤的女生
icon.png阅读文章
icon.png[黑特]
icon.png[问题] SBK S1安装於安全帽位置
icon.png[分享] 旧woo100绝版开箱!!
icon.pngRe: [无言] 关於小包卫生纸
icon.png[开箱] E5-2683V3 RX480Strix 快睿C1 简单测试
icon.png[心得] 苍の海贼龙 地狱 执行者16PT
icon.png[售车] 1999年Virage iO 1.8EXi
icon.png[心得] 挑战33 LV10 狮子座pt solo
icon.png[闲聊] 手把手教你不被桶之新手主购教学
icon.png[分享] Civic Type R 量产版官方照无预警流出
icon.png[售车] Golf 4 2.0 银色 自排
icon.png[出售] Graco提篮汽座(有底座)2000元诚可议
icon.png[问题] 请问补牙材质掉了还能再补吗?(台中半年内
icon.png[问题] 44th 单曲 生写竟然都给重复的啊啊!
icon.png[心得] 华南红卡/icash 核卡
icon.png[问题] 拔牙矫正这样正常吗
icon.png[赠送] 老莫高业 初业 102年版
icon.png[情报] 三大行动支付 本季掀战火
icon.png[宝宝] 博客来Amos水蜡笔5/1特价五折
icon.pngRe: [心得] 新鲜人一些面试分享
icon.png[心得] 苍の海贼龙 地狱 麒麟25PT
icon.pngRe: [闲聊] (君の名は。雷慎入) 君名二创漫画翻译
icon.pngRe: [闲聊] OGN中场影片:失踪人口局 (英文字幕)
icon.png[问题] 台湾大哥大4G讯号差
icon.png[出售] [全国]全新千寻侘草LED灯, 水草

请输入看板名称,例如:Boy-Girl站内搜寻

TOP