实事求是的讲,写《【野生程序员】:优先招聘》的时候,
想一想招聘启示里你们要求“计算机专业本科以上学位”我“无计算机专业相关专业文凭”优先然后你们就炸了我们没有歧视你这才是歧视你自卑你愤青你酸你难成大器……我无力反驳只是想说每个人的言行都是他心灵的镜子。谢谢你们其实我没有想挑起科班/非科班之争虽然可能结果会超出我的预料我的本意是想给“非科班”的同学鼓气缓解他们身上的压力让他们看到希望给他们力量让他们相信完全可以在更艰苦的环境下自学成才而且结果不会比“科班”的差但你一定要委下身段踏踏实实的去学一步一个脚印的去做自卑自大争吵辩驳都无助于你的成长。请牢记言语没有力量另外愿意听一句的“科班”同学“无计算机专业相关专业文凭”优先并非完全出于义愤。都是筑基你是名门大派用资源用丹药堆出来的他是一路苦修战斗领悟突破的你觉得谁更有潜力所以啊放下那些虚荣骄傲真正的去战斗吧毕业三年以后是没人再看你的学历的。另外声明一点对老赵没有任何意见除了景仰。他针对的是培训机构我完全明白但仍然不能赞同。所以我说“每一次看到这一段文字我的心里就会有一种难以言表的复杂情绪”至于如何复杂不是说了吗难以言表啊。好心平气和之后我们继续讨论技术问题。在带队的过程中性能的问题还比较好解决最消极的想法“好啊多一事不如少一事你让我不管还不简单”但要求写测试代码那就炸锅了以我的经历“测试驱动”是一个最具争议的话题没有之一。吹捧者和反对者泾渭分明而且都有大量的论据和证明。记得博客园曾经有一篇文章大意是“公司付钱给你不是让你写测试代码的”下面一片狂赞。在我自己的项目开始的时候我是放弃了测试驱动的呵呵还找到了原文里面总结得很准确最大的原因是“懒”。但最后让我下定决心开始“测试驱动”实践的是我一次花了两天一夜都没调出一个Bug垂头丧气筋疲力尽之后无可奈何的接受了这个现实测试还是很有用的——即使是自己写的代码。我之前的系列博客也已经反复的强调架构是一种“无奈”是现实是问题驱使你去做一些其实你本来不想做的事情。你无法理解一些看起来像“脱了裤子放屁”一样的行为通常只是因为你没有遭遇过那些现实那些问题。看看大学能教你这些东西么即使你没有多少开发经验你也应该能够想象单元测试最大的问题就是它需要花时间花精力去写那么这个花费是否值得呢这还是由你架构的目标决定的或者你的需求决定的。如果系统是一次成型交付使用此后几乎不会更改的那么一次性的手工测试就够了但如果你的系统是会被“千锤百炼”的不断折腾修改的那么这个测试就是很有必要的。最简单的考虑每一次更改我都要手工测试一次那还不会如我多花点时间弄个“自动化”的东西出来。单元测试其实就可以理解为一种自动化的测试工具。但是“自动化”的理由还远远不够。因为你马上想到的每一次需求变更代码调整测试代码也得相应的改呀没有测试代码我就只需要改开发代码现在有了单元测试我还得再改测试代码。本来我只维护一套代码现在我凭空增加了一套代码也需要维护这不是增加了维护成本不是和你“可维护性”的架构目标背道而驰了么是一套代码好维护呢还是两套代码更好维护这是一个非常好的问题适用于很多情景比如分层架构你说分层解耦实际上还不是一改就得从UI层改到数据库每一层都得改。我能给出的回答大概有一、无论有无单元测试开发代码进行修改之后是不是都要进行测试没有单元测试并不代表你的代码就不需要测试了只不过是你手工的去测试了一遍而已。切记你的工作并不只是把代码写出来而已二、进行手工测试和更改单元测试两者的耗费比会根据测试重用的次数而变化。一次手工测试可能需要5分钟跑完更改单元测试代码可能需要20分钟但如果这测试会跑100遍单元测试完胜手工测试。三、你说哪里哟什么功能会改100遍我没说你的功能会改100遍我说的是测试会跑100遍。有区别么你可能还在犯迷糊是吧好吧我们讲个故事。有一个小伙子他很不情愿写测试代码。老板拿他没辙啊也没那么多精力和他磨牙于是老板自己写单元测试。这小伙子的代码提交之前要review老板总能一次次的找出它代码的问题。他改的是登录老板告诉他积分系统被他改出了问题他又去改积分老板又告诉他消息通知系统被他改坏了他又去改消息系统老板告诉他登录还是有问题……于是他崩溃了“这TM什么一个烂系统”最终他终于回过神来了为什么老板总能知道这里的改动会影响那里呢老板的思维有这么严谨老板躲在一旁偷笑就不告诉你“其实我就是跑了一遍单元测试而已”。这个老板就是我。我故意的就不一次性的告诉他所有的问题就要这样一次次的折磨他让他的痛苦能刻入骨子里去。最后我还要问他你现在对你的代码是不是还那么自信如果没有我的review我也是靠单元测试你能不能发现这些问题如果我们的项目已经部署到生产环境而且你的改动带来的破坏没有被发现就上线了会带来什么样的后果这一次他服气了。后来他用NUnit用得麻溜麻溜的。每一次改动如果有意想不到的未通过test case他都会很激动的给我张截图顺便发发牢骚。我微笑不语那种满屏绿灯通过的踏实和意外爆出红灯之后的惊喜没有经历过的人是无法体会的。所以其实当对象间的关系变得越来越错综复杂像一张密密麻麻的网一样之后一个局部的改动就很有可能会触发极其复杂的连锁反应。所以为了保险起见所有可能相关的组件都应该进行测试所谓的“回归测试”。这时候如果只有纯粹的手工测试会面临两个问题难以确定测试的边界那些部分可能会被影响这得我们脑袋凭空硬想啊兄弟极大的测试耗费。而且这种耗费是相当的无聊繁琐伤人心的——没人愿意做这种事。据说所知现在很多公司测试人员的工资已经比开发人员还高了。为什么简单枯燥无聊没人愿意做啊好的我假设你已经认识到了单元测试的重要性并开始摩拳擦掌跃跃欲试。接下来我得给你泼一大瓢冷水单元测试不是那么好写的从某种程度上讲写单元测试比写开发代码还难。难得我工作的所有公司没有一家有过成功的案例。大概是几年前我在公司修bug老大告诉我“你这个功能比较核心跑一下单元测试吧”。“哇塞我们有单元测试”一种高大上的感觉迅速弥漫全身终于见到传说中的Unit了捣鼓了一会能跑了试试看——我的个妈呀怎么这么多红灯我真被吓住了这都是我的改动造成的老大就是老大不慌不忙“数一下有多少个通不过”“啊”我以为我听错了数多少个通不过有什么用得把他们全部弄通过啊搞了一会儿才终于弄明白了把我改动前后的代码分别跑一遍对照一下通过失败是不是一样的只要是一样的就OK了。比如以前是8个通不过现在还是8个通不过这样就可以了我一直不明白为什么不把那8个通不过的单元测试给弄成通过呢这样摆着究竟算什么直到我自己开始写单元测试。坑爹啊到处都是坑跳出小坑进大坑大坑下面还连着小坑前面是坑后面是坑一堆一堆的连环坑……单元测试写出来容易跑过难而且跑不过的原因还不是你的开发代码逻辑错了而是测试环境/数据出问题。要测试一定要有数据这个数据的构建完全不是我们所想象的那么简单。以我们创业家园项目里的积分系统为例假设一个简单的需求博客被点赞博客的作者应该获得一定积分该积分数量是由点赞人目前所有的可用币转换而得来的已简化具体可参考文档积分。要准备的数据就有博客一篇要有作者作者已有积分点赞人一名有一定数量可用币。如果只是这样还可以接受但其实下面会有一堆的问题作者的积分从哪里来我们的开发代码出于封装的考虑用户的积分是只读的你单元测试怎么设这个值要么写代码模拟作者通过其他行为发布文章回答问题等获得积分这将开启新一轮噩梦如果用Mock或者反射强行设置事实上省略了作者获得积分的历史所以用户“积分历史”为null之后对其“加积分”时就会报异常。更坑的是你以为你什么都处理好了的时候你突然悲哀的发现这个博客得首先“被发布”而博客一经发布其作者就获得了一定数量的积分所以你以前设置的积分又变了……点赞人的可用币同样可能遇到类似的问题。可用币怎么设置设置之后会不会在跑测试时被意外更改点赞的行为被封装成一个方法运行这个方法会检查点赞人之前是否已经对该文章点过赞所以还应该有一个“点赞历史记录”哪怕是空的都得new一个否则就空异常……反正当时是写得我直接摔了鼠标写得憋屈啊而且我还是完全隔绝了数据库的真不知道那些要从数据库里取数据来跑单元测试的是怎么做的这时候我一下子就明白了实际工作中加班赶进度一个接一个的填坑连重构的时间都没有怎么可能还挤得出时间来写单元测试就算开始雄心勃勃的写了随着系统日益复杂维护单元测试的成本也与日俱增甚至复杂度更甚开发所以放弃也就成了绝大多数项目的唯一选择。在公司上班么大多数人都是这样的能推就推。我们开发写完了代码基本上能跑了就该交给测试人员了呀天经地义的嘛是不是而且测试的时间是不会计算到我的项目开发时间里的我总算是按时完成了开发任务。累坏了休息一下让测试的忙活去吧哈哈……但我是个光杆司令我没测试人员啊曾经有那么一两个时候我真准备招一两个测试人员的。但好在我天生的节俭美德也就是“抠”啦让我冷静下来。我就想啊测试只能告诉你出了bug不能告诉你根源啊。没有单元测试我单步调试不也折腾了两天了么这是系统本身的复杂性或者代码组织的不合理造成的不能归咎于单元测试。不还是有这么多开源代码都有详尽的单元测试么他们是怎么做到的呢在单元测试上的付出最终一定会获得超值回报想想没有单元测试的公司那超级庞大的测试团队或者四处冒烟的系统你愿意走这么一条路么所以我不断的告诫自己不要着急冷静细致。终于一步步抽丝剥茧把这一团乱麻一点点的归纳整理最终还真被我找到了一条路子一个个的单元测试都慢慢完成通过了开发代码里潜在的一些问题也浮出水面被我一个个的消灭。最后再跑一遍单元测试一路绿灯哈哈更奇迹的是困扰我两天的bug不知道什么时候消失了后来我看到这样一种说法可测试的代码不一定是好代码但坏代码几乎是不可能被测试的。深以为然深度耦合的代码写他们的单元测试难于上青天但反过来我们可以以可测试为标准不断的完善重构开发代码只要这样坚持下来最终代码的质量怎么都不会差到哪里去。所以于我而言单元测试是否有价值的争论可以休矣不如换个角度想一想怎样才能把单元测试坚持下去。最后如果有心的同学就会注意到我一直用的是“单元测试”而不是“测试驱动”。因为测试驱动是一个更广阔的概念是一个更崭新的天地单元测试只是其中的一小部分在下一篇博客我会讲解我是如何试着将测试驱动的概念运用到项目开发管理中去的。这里需要强调的一点先写测试。一上手就写开发代码写完了才写单元测试。这是很多开发人员的习惯我也经常犯这样的毛病一不留神就忘了。这样做最大的问题就是没有真正实现“测试驱动”。你实际上还是由开发在驱动那么很自然的测试照着开发的if...else...写一遍有什么意义呢这样做下去就会不断的强化“测试无用累赘”的印象因为测试就是简单的把开发代码重写一遍而已。我开的药方是单元测试代码和开发代码由不同的人员编写如果做不到上面一点先写单元测试如果连上面一点也做不到直到出了bug了再写单元测试第三条可能有同学无法理解不是说单元测试很重要么为什么要等到出了bug才写答案是偷懒呗记住我们程序员是世界上最懒的人没意义的事从来不做你先写开发代码再写测试真的没意义没意义就干脆不要做了。但你可以开启“乐观模式”或者“Lazy模式”先乐观的认为我的代码没问题或许真的就没问题呢是吧如果真出了问题做一个补救这个时候就应该用单元测试把这个问题表现出来因为他根据墨菲定律它这里出了问题以后就很有可能继续出问题。这个时候就不要再偷懒了。未完待续