开发测试一山容得二虎,30条有关测试的故事和事故

软件工程规则和测试积累的最佳实践,能帮助企业显著节省时间,减少痛点。良好的测试实践既可以确保质量标准,也可以指导和塑造每个程序员的发展。

小数今天推送的这篇文章,文中的许多原则都与测试实践和理想有关,有一些还是Python特有的。正如本文作者所说,程序员的那些固执己见,强烈的意见表达和坚持,往往是激情的最好表现。

  1. YAGNI:不要编写现在尚不需要,而将来可能需要的代码。现在的代码不可避免地会变成死代码或需要重写,因为未来的用例总是与设想的不同。如果把代码放在未来的用例中,在代码审查中要提出质疑。(例如,可以并且必须设计API来允许将来的用例,不过,这是另外一个问题。)

注解代码也是如此。如果一个注释代码块进入发行版,它就不应该存在。如果是可以恢复的代码,创建一个ticket,并引用代码删除的提交散列。YAGNI 是敏捷编程的核心元素。Kent Beck提供的极限编程解释(Extreme Programming Explained)就是最好的参考。

2.测试本身不需要测试。用于测试的基础架构,框架和库需要测试。除非确实需要,通常不要测试浏览器或外部库。测试你写的代码,而并非其他人的。

3.第三次编写相同代码时,是将其抽取到通用帮助器(并为其编写测试)的正确时机。测试中的辅助功能不需要测试; 当把它们分解出来并进行重用时,才需要测试。到第三次编写类似的代码时,会清楚地知道正在解决什么样的问题。

4.涉及到API设计:简单的事情简单化; 复杂的事情可能如果可能也简单化。首先设计一个简单的情况,如果可能,最好使用零配置或参数化。为更复杂和更灵活的用例添加选项或其他API方法。

5.快速失败。尽可能早地检查输入,并在无意义的输入或无效状态上失败,使用异常或错误响应,来确定问题。允许代码“创新”,也就是说,通常情况下,不要对输入验证进行类型检查。

  1. 单元测试是对行为单元的测试,而不是对实现单元。改变实现,而不是改变行为或被迫改变任何测试,当然这点并不是总能实现。在可能的情况下,将测试对象视为黑盒子,通过公共 API 进行测试,而无需调用私有方法或修改状态。

对于一些复杂的场景,比如一些特定复杂状态下,测试行为找到一个难以理解的错误。编写测试确实有助于解决这个问题,因为它会迫使你在编写代码之前,考虑清楚代码的行为以及如何测试。测试首先鼓励更小,更模块化的代码单元。

7.对于单元测试(包括测试基础结构测试),应该测试所有的代码路径。100%的覆盖率是一个很好的起点。你不能涵盖所有可能的排列/组合状态。在有很好的理由时,代码路径才能被测试。缺少时间不是一个好的理由,这往往导致最终花费更多时间。好的理由包括:真正无法测试,在实践中不可能实现,或者在测试中其他地方被覆盖。没有测试的代码要承担责任。衡量覆盖率,并拒绝降低覆盖率的百分比是朝正确方向前进的一种方法。

8.代码是敌人:可能出错,需要维护。少写代码。删除代码。不要编写你不需要的代码。

9.随着时间的推移,代码注释不可避免地成为谎言。实际上,很少有人在事情发生变化时更新评论。通过良好的命名实践和已知的编程风格,努力使代码有可读性和自我记录。

不明确的代码确实需要注释,比如围绕一个模糊的错误或不可能的条件,或者必要的优化。

10.防守地写。要总是想可能出现什么问题,无效输入会发生什么,什么可能导致失败,这有助于在错误发生前发现错误。

11.如果逻辑无状态且无副作用,则易于进行单元测试。将逻辑分解成独立的函数,而不是混合成有状态和副作用的代码。将有状态和带副作用的代码分离成更小的函数,可以更容易地模拟和单元测试。副作用确实需要测试,测试一次并在其他地方进行模拟,是比较好的做法。

12.Globals are bad。功能优于类型,对象比复杂的数据结构更好。

13.使用 Python 内置类型和方法比编写自己的类型更快(除非用C编写)。如果考虑性能,尝试解决如何使用标准内置类型而不是自定义对象。

  1. 依赖注入是一种有用的编程模式,可以清楚了解依赖关系。(让对象,方法等接收依赖关系作为参数,而不是自己实例化新的对象。)这是一种交换,使API签名更加复杂。如果完成依赖项需要使用10个参数,说明代码太多了。

  2. 越需要模拟测试代码,它就会越糟糕。需要实例化和放置的代码越多,用来测试特定的行为,代码就越糟糕。测试的目标是小型可测试单元,和更高级别的集成和功能测试,来测试这些单元是否正确地协作。

16.面向外部的 API 是要“预先设计”的,它关系到未来用例。改变 API 对自己和用户来说都是一种痛苦,而创造向后兼容性是非常可怕的,尽管有时不可避免。精心设计面向外部的API,仍然坚持“简单的事情简单化”的原则。

17.如果一个函数或方法超过30行代码,就要考虑分解。好的最大模块大小约为500线。测试文件往往比这个更长。

18.不要在难以测试的对象构造函数中工作。不要把代码放在init.py中。程序员不希望在init.py找到代码。

  1. DRY(Don’t Repeat Yourself不要重复自己)在测试中比在生产中重要得多。单个测试文件的可读性比可维护性更重要(突破可重用的块)。这是因为测试是单独执行和读取的,而不是作为大系统的一部分。过多的重复意味着可重用的组件可以方便地创建,但是与生产相比,测试的重要性低得多。

20.当看到需要并有机会时进行重构。编程是关于抽象的,抽象映射越接近问题域,代码就越容易理解和维护。随着系统有机地增长,需要为扩展用例而改变结构。系统超出抽象和结构,而不改变它们成为技术债务,这会更痛苦,更缓慢,更多bug。包括重构成本,包括对功能的预估。你把债务拖得越久,它积累的利息就越高。Michael Feathers 撰写了一本关于重构和测试的书籍,其中着重介绍了遗留代码。

21.正确的编写代码第一,速度第二。处理性能问题时,请务必在修复之前进行配置。瓶颈通常出乎你的意料。编写难懂的代码如果是为了速度更快,为此你进行了配置并证明这么做是值得的,那么也仅仅是速度上值得而已。编写一个测试,训练正在配置的代码,让它知道什么情况下会变得更简单,并且留在测试套件中,来防止性能下降。(通常情况下,添加计时代码总是会改变代码的性能特点,使性能变得经常差强人意。)

22.更小范围的单元测试在失败时会提供更有价值的信息。要判定错误,有一半的系统测试行为的测试需要依据更多的调查。一般来说,运行时间超过0.1秒的测试不是单元测试。没有所谓的慢单元测试。通过严格范围的单元测试测试行为,你的测试可以作为代码实际规范。理想情况下,如果有人想了解你的代码,应该能够将测试套件作为行为的“文档”。

  1. “这里没有发明”并不像人们所说的那么糟糕。如果我们编写代码,我们知道它的作用,知道如何维护它,可以自由地扩展和修改它。这是遵循了YAGNI的原则:我们有需要用例的特定代码,而并非通用代码,通用代码会给我们不需要的部分带来复杂性。另一方面,代码是敌人,拥有更多的代码没有好处,当引入新的依赖关系时需要考虑权衡。

24.共享代码所有权是目标。孤立的知识没什么好,这意味着至少要讨论、记录贯彻设计决策和实施决策。代码审查时开始讨论设计决策是最糟糕,因为在编写代码之后进行彻底的更改太难克服了。当然,在Review时指出和修改设计错误,比永远不总结不思考要好得多。

  1. Generators rock! 与有状态的对象相比,它们用于迭代或重复执行通常更短,更容易理解。

26.让我们成为工程师!设计、建立强大和良好实施的系统,而不是增加有机怪物。编程是一个平衡的行为,我们并不总是建造火箭飞船,过度设计(onion architecture洋葱架构)与设计不足的代码同样令人痛苦。罗伯特•马丁(Robert Martin)所做的几乎任何事情都值得一读,“ 干净架构:软件结构和设计指南”( Clean Architecture: A Craftsman’s Guide to Software Structure and Design )是这方面的好资源。设计模式( Design Patterns)是每个工程师应该阅读的经典编程书籍。

27.间歇性地失败的测试会侵蚀测试套件的价值,以至于最终每个人都忽略测试运行的结果。修复或删除间歇性失败的测试是痛苦的,但值得努力。

28.一般来说,尤其是在测试中,等待一个具体的改变,而不是任意时间内sleeping。Voodoo sleeps 很难理解,而且会放慢测试套件。

29.至少要看到你的测试失败过一次。将一个蓄意的 bug 放入让它失败,或在测试行为完成之前运行测试。否则,你不知道你真的在测试。偶尔写写测试代码实际并不测试任何东西,或写永远不会失败的测试,都是很容易的。

30.最后,管理要点:持续的功能研发是开发软件的一个可怕方法。不让开发人员在工作感到骄傲, 就不会从中获得最大的利益。不解决技术债务,会拖慢开发速度,并导致更糟糕的、更多bug的产品。

原文作者:Michael Foord
原文链接:
https://opensource.com/article/17/5/30-best-practices-software-development-and-testing

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容