写测试代码这种事情 ,之前只在网上和书上看到过, 本身历来没有写过。 每当看到那些世界顶级程序员编写的技术书籍中出现“测试用例”“测试代码”的字样或者一些行业的鼎鼎大名的技术大牛们说起写测试的重要性的时候,个人内心就会产生一种本身编的必定是假程的错觉, 为何我写代码就历来不用那玩意?程序员
就拿开发一个MVC框架的Web应用程序设来讲, 一般的作法就是新建一个控制器和一个模型, 把代码要实现功能的业务逻辑写在模型里面,控制器调用模型, 假若有外部参数则接收参数传递给模型, 假如业务逻辑过于复杂致使模型过于臃肿或逻辑不畅, 则再进行梳理或提取,构建成一个新类,再由模型进行调用。 这一过程反复循环迭代, 直到功能开发完毕。 调试或者测试写的代码是否能得出想要的结果, 天然也是使用最简单粗暴的方法, 在浏览器中运行程序, 定位到控制器, 控制器调用模型, 模型再调用其它所涉及到的类,拿到结果后再一步步返回, 浏览器是否显示预期结果就意味着咱们写的程序是否正确。无论是咱们要测试的功能模块离控制器只有一个调用仍是有十个调用,都遵循着这样一个步骤, 由于这是最符合咱们直觉和习惯的方式。 我也一直以这种方式在开发程序。编程
本来这也没有什么问题,咱们所写的代码逻辑是经过咱们的大脑深思熟虑组织后产生的,一般状况下咱们有这个把握能够肯定代码逻辑运行的正确性,就算出现意料以外的状况, 多点几下浏览器的刷新按扭也能把问题找出来解决,由于咱们对代码的运行逻辑了然于胸,自信不会出什么叉子,一旦出现了叉子那就产生了所谓的程序BUG。浏览器
然而, 万事总有例外, 致使咱们以往的经验失效。 就拿我最近碰到的一件事情来讲,公司有一个项目因性能优化须要,对部分功能进行技术方案调整, 重写了代码。代码量不大, 功能自己的代码和其依赖的通用函数代码加起来一共也就二三百行,可是隐含在背后的逻辑却异常复杂,涉及到的数据表也有五张。我将这部分须要重写的代码重头至尾仔仔细细读了一遍, 勉强能理解每个语句块都干了些什么。 多是我逻辑思惟能力不过关, 也有多是代码太过于复杂 , 我没有办法将全部这些代码的前因后果全盘了然于胸,也就没有办法从全局的角度去梳理代码逻辑肯定优化方案,我只能从局部的角度出发, 依样画葫芦的按照旧方案从新实现一遍代码的逻辑, 在实现的过程当中如发现有优化的余地则进行局部优化,等到足够熟悉全局逻辑后,再从宏观的角度对代码结构进行调整优化,这么作效率是低了点,倒是最保险的作法。安全
我照着旧代码写出一个个如出一辙的函数,却没有办法肯定这些函数的运行结果是否能得出预期的结果,鬼知道换一种语言实现之后, 函数吐出来的结果仍是不是和以前的同样,我可没有jeff dean那样牛逼,预判代码的结果比编译器还精准。原本这也不是什么大问题,把代码跑一遍,当执行到这些函数的调用时天然就知道结果了。问题出在这之中某些函数和代码的入口隔着七八个调用,并且其中某些调用由于依赖于某些if条件判断结果而不是必然被调用到的,要构造出能使这些函数被调用到的if条件判断分支走向的参数环境是一件异常繁琐的事情,光想一想就让人以为烦躁和睦馁。另外一种方法就是把函数的调用代码复制一份放到执行入口的开始位置,这样代码一运行就直接能调用的到了。 然而, 这种方法也会带来问题,如性能优化
函数处于不一样的类和包内,调用函数须要导入包和实例化类,而作这些事情对项目的自己没有实际的意义框架
某几个函数只在所在的类内被调用, 访问修饰是private, 经过这种方法测试它的准确性还须要放开权限把访问修饰声明为public, 调试完毕后还得改回去, 操蛋函数
有多个分布在不一样类之中的不一样函数须要以相似的方式测试, 反复进行这些无心义且繁琐的操做, 极度浪费时间,影响心情性能
代码的执行入口总放着那么一坨被注释掉的代码,想拿掉又怕拿掉之后下次还要用, 心里挣扎的难受单元测试
所以, 想要解决这个问题, 上面的两种方案都不可取, 柔肠百转也想不出像样的解决方案。 长辈们都说编程都是脑力劳力, 我以前不觉得然, 但当碰到这些想破脑壳也找不到办法的问题时就不得不认可, 编程的确是脑力劳力。我才20岁,外表却有30岁能够看,我想也跟长期被这些问题困扰有必定的关系(我说的是10年前的本身)测试
我思前想后,检索全部脑子中关于程序设计的资源, 才找出一个以前历来没有尝试过的方案, 引入单元测试。我这我的有一个优势, 在工做上碰到陌生的东西历来不会望而却步,只要有用处, 都会去积极尝试。对于单元测试,我虽然没有掌握使用的方法, 可是网上查查资料, 看看教程, 我相信花不了多少功夫就能搞出来。 事实也的确如此, 只看了一篇资料,照着教程的步骤操做就把测试程序跑起来了。 我使用的是go语言, 按照go test的规则 ,被测试的代码所在的文件名加上test后缀便可做为测试代码所在的文件的命名,以下图
测试函数的命名方式必需要以Test做为前缀, 以下图
测试代码编写完成后, 在代码所在的文件目录下使用cmd运行go test命令,测试代码就可被运行了
须要测试的函数在测试代码中被直接调用, 省去了跟踪庞杂代码执行走向的麻烦,从复杂的业务逻辑中解放出来, 很是的清晰方便。
从表面上看, 写测试代码的好处就是方便测试函数的正确性, 然而, 随着以后代码的编写, 我发现写测试代码所带来的好处不止于此。当有了要为代码编写测试用例的前提条件后, 我在实现某个函数时就约束本身, 这个函数必需要方便编写相应的测试代码。有了这层约束之后, 我发现写出来的代码的质量要比不写测试用例时高, 好比
函数的功能职责更加单一了,换言之, 函数的逻辑更稳定了, 不易产生变更, 由于我不想我辛苦编写的测试代码随着函数的代码的调整而付之一炬。
不会很随意的把代码乱放, 写出来的代码更加整洁,该提取函数时就建新函数, 该内联函数时则删除没必要要的函数,在以前, 为了偷懒每每会对这些细节视而不见, 这会加速代码的腐烂。
更早的发现BUG,不少时候, 程序的BUG都是在生产环境中由用户发现,缘由很简单, 开发项目的速度和质量这对冤家之间程序员每每会选择前者,此外, 程序员会毫无根据的信任本身写的代码,所以当向程序员反馈BUG时,他们都会保持怀疑的态度。不少时候, 程序员写一个函数一般只给一个特定的输入,运行后发现输出如本身预期那样后就默认这个函数是健康的, 事实上, 当给这个函数另外的输入时, 函数吐出的结果就在预期范围以外, 这便致使了BUG的产生, 个中缘由即是对本身直觉盲目的信任, 认为本身的大脑就是一我的肉编译器。 编写测试能够很大程度上的杜绝这类问题
一般,咱们会认为编写测试是一件浪费时间的事情, 而后就是一边向别人吹牛一边则啪啪啪的打本身脸。 除此之此, 在开发项目时经常以逻辑不稳定随时须要调整代码为理由拒绝写测试,然而, 当从相反的方向来考虑问题时会发现, 有了测试的约束后,咱们会更加仔细和严谨去编写每个函数 ,逼迫本身更加深刻的考虑问题而防止代码走样, 提升代码质量、安全性以及稳定性, 这也是写测试所带来的相当重要的意义。