真没想到,高伯还是给了我两百块报酬(土豪就是土豪)——我还担心他连运营成本都不肯给呢,毕竟网站做得并不是令人太满意。为社团做了两年的网站,这是第一次收到报酬,也算是自己赚的第一桶金吧。

事实证明,临去北京前我给 cc 开放了管理者权限是一个非常正确的选择。尽管 wisecity 已经举办了两届,尽管在此之前我已做过 4 个网站,尽管这次的网站是在上一届的基础上进行改造的,我的心里仍有些忐忑不安,害怕它会出什么问题——抑或是大会的工作人员操作不当导致网站崩溃云云(这事真的发生了)。5 天的 wisecity 大会,我都身在北京,身边没有电脑,没有解决问题的条件,因此需要一个有基本的编程能力的人协助我。5 天下来,问题还不少,调试的过程可谓是十分原始:接到问题后,囿于有限的条件,我只能通过翻看手机上的代码,在脑海中模拟并觉出问题所在,找到最小代价的改动方案,再将改动之处通过短信通知 cc——当然,很多时候并不那么顺利,我还需要跟多的信息帮助判断,比如错误日志,又比如进行特定操作时的表现(这个最坑爹,可遇而不可求)。我尽一切努力在脑海中想象着程序的流程,很累,但也很有挑战性。

第一天就遇到了一个棘手的问题:高伯在删除测试数据时不小心把管理员账号(admin)一起删掉了。这个消息从扬声器传出后,我足足沉默了一分钟,心里像是有千万头草泥马奔过:

怎么这么毛手毛脚?这下可好了,麻烦大了——虽然这个设计是有些不合理,但这不能怪我,django 已经封装好了(姜戈:怪我咯?)。快想办法快想办法……直接去后台加一个?不行不行,密码这块很难弄,MD5 + SHA1 + Salt,根本不是人能算的——只能让 django 自己产生一个用户了。嗯,我需要一个 MySQL,一个完整的 Python 开发环境,各种包……不知 cc 能不能应付过来。真是的一大早!@#$%^……

挂断电话,抄起手机噼里啪啦给 cc 发了一条短信,手把手教配置 MySQL,django,导出 SQL,上传数据库……吧啦吧啦洋洋洒洒几百字。发送时一直祈祷:但愿 cc 悟性高一些,不然真的完了——还好,半个小时后,前线传来了好消息:一切顺利。

总算是松了一口气,才发现,背已汗湿。

然而,事情还没有结束。

第二天一早,我又被告知:上传的文件下载不了(纳尼?走之前不是才作过测试吗)。点开文件看网址,发现原来是七牛的域名后面少了一个/,很快便处理好了。本以为可以好好玩一天了(那天出去玩),逛了大半个北京,夕阳西下时,却又接到一个 bug:选手列表只显示了 10 个用户,但应该有 40+ 个。

这个 bug,前后调了 2 个小时。从奥林匹克公园,到中关村——因为,我并不知道问题出在哪,一点也不知道。打 console.log,翻看网络记录(没错!在短信这种高时滞的通讯条件下!),最终发现,原来是 REST 返回的用户列表顺序反了,加载了最后 10 个,但由于用户没有明显的顺序标志,所以调试时没有察觉。改正之后,终于可以安心吃完饭——

谁知,两分钟后,短信又开始轰炸了:加载的用户列表有重复项!很多的重复项!!没有规律的重复项!!!

这个 bug,前后又调了 2 个小时。从中关村,再到人大附中,直到繁星爬满了苍穹。第一个反应是自制的瀑布流控件滚动事件并发处理没有做好。翻看源代码,似乎找到一处疑似有问题的地方,尝试让 cc 改了一下——半个小时后,传回了一个令人泄气的消息(中间配置grunt又耗费了好些功夫)。冥想了一个小时,前后端的代码都看遍了,就是没有发现问题,最终只得放弃,告诉高伯用一些奇技*巧避开重复项。直至回来后,静下心分析代码,才发现在一个不起眼的地方有一处笔误,这是重构 HFMUN 的瀑布流控件时产生的。

之后的几天,又有几个大大小小的 bug,抑或是需求改动,处理得还算顺利。可怕的是最后一天,KVDB 直接宕掉了——我整个缓存都是挂在 KVDB 上的,也怪当时没有作容灾处理,这个事件直接导致全站报 500 错误。急急忙忙关掉了缓存(还好做缓存时解了耦,只需改动两处即可),然后再质问 SAE,这些都是后话了。

然而,这里有个值得反思的问题:既然我已经作过 4 个网站,为何还是会有 bug 出现?从 HFMUN 1.0,到 wisecity 1.0,到 HFMUN 2.0,随着我技术的逐渐成熟,bug 出现的频率也在不断下降——但是 bug 仍在,就像“杀不死的小强”一样。

测试,关键还是在测试。

人的主观意识,受制于时空、环境等诸多因素,任何一个参数的改变都有可能影响主观能动性的发挥。因此,不能保证在任何时候人的意识都能正确地、高效地发挥作用。软件工程,作为人类纯意识的产物,其正确性并不能百分百地保证。或是精神不振引起的一处笔误,或是重构迁移时没有同步更改的一处配置,抑或是一处自己在开发过程中完全没有意识到的错误——设计归设计,程序是否能运行,还是电脑说了算。许多的 bug 就是这样产生的。

这个时候,单元测试(Unit Test)就显得非常重要了。通过分析需求而设计的测试样例,可以保证功能的相对正确性,即在能够考虑到的所有情况下,程序都能狗正常运行。这是重构(Refactory)过程中十分重要的一个环节,因为外部不变性是重构必须遵守的一个准则。

在软件工程的上古时代,测试常常是由人工来完成的。团队中,总有几名成员每天都在做着重复、机械的工作,即对新增的功能或是修改过的功能进行测试。这种测试机制费时费力,同时也不是非常有效——上面已经说过了,没有人能保证主观意识的正确性。后来,出现了基于脚本的批量测试,测试人员可以编写一小段代码对特定的功能进行校验,很大程度地提高了效率;再到今天的分布式测试,成熟的测试系统可以模拟多种不同的生产环境,检测到代码库的变化后,便会自动进行单元测试。这是单元测试的自动化进程。

今天的自动化测试固然很成熟,基本可以检测到各种逻辑错误。但在测试领域,却仍有一处令其束手无策的“禁地”——这便是 GUI 测试。综合分析 wisecity2.0 的 bugs,其中大部分都是前端出了问题。GUI 测试的麻烦在于:

  • GUI 是一个输入与输出交替进行的系统,并且输入具有无限的可能,无法用有限的测试样例对输入进行覆盖。比如第二天发生的那个 bug 需要在“文件上传成功后,跳转到首页,点击下载链接”、“用户数量超过 10 个,在列表页面快速滚动鼠标滚轮”才能被触发。许多的 bug 只有在真实生产环境中被用户捕捉到。
  • GUI 的正确性没有一个绝对的判定标准。元素错位、颜色不正确,这些事件都不报错,但它们也是 bug,通常也只有人类认为它们是 bug。但它们的发生并不影响功能,只是用户体验(User Experience)不好——用户体验是一个纯主观的概念,至少在当下,计算机是不能理解的。

目前,GUI 测试主要还是依赖人工。Facebook 就有一个庞大的测试人员系统,以模拟尽可能多的用户操作样例。诚然,业界已经开始出现一些 前端自动化测试框架,有如 selenium、phantomjs 等通过 mock 事件模拟用户操作,更有甚者如 PhantomCSS 可以对特定操作的结果进行像素比对,可软件的主观部分还是需要人脑来判断。

这是一个好时代,周围的一切都在飞快地变化着。希望在不久的将来,当人工智能出现时,这个问题能够有效地解决。