这几天在写一个采集程序,用nodejs。老实讲这是第二次(相对正式)写和采集有关的模块,第一次是因为要定时访问自身项目来触发缓存的,细节可以看这里。后来从上一家公司离职,也没有继续维护这个程序了。
  这次算是正宗的采集模块(确实是从目标网站中获取有价值的内容,我不方便透露具体细节了~),而且这个项目是正式产品,不像之前公司那样扯~~这篇文章本身不是讲采集程序细节的,其实个人感觉开源的采集系统已经很成熟了,如果不是有特别特殊的需求,还是不要重复造轮子的好。
  我们回到主题,讲一下测试。但在这之前呢,我还是要?嗦一点(毕竟是总结嘛~):产品。如果你做的是练习项目(毕业设计),或内部使用的小工具,又或是一个垃圾网站外包(总价才1000块),你可能会选择快糙猛的直奔功能去。这没错,我也会这么做(虽然可能强迫症患者(或道德模范)会觉得这么做太丢份儿)。但正式的产品,还是需要认真对待的。那怎么才叫认真对待呢?那是所有产品相关的事物都要有: 监控和测试 。
  现在很多云厂商提供的所有服务都有配套的监控功能,国内外还有专门做监控服务的公司,此外还有很多开源的监控系统(Open-Falcon初探)供我们选择,所以真的没有什么借口不去做监控。不过,还是推荐使用成熟的商用监控服务,因为监控平台自身的高可用和低性能损耗是非常重要的,也是技术含量较高的。
  终于要说测试了,之前我写过几篇和测试相关的文章:
  小团队玩不转的测试
  casperjs+mocha+chai搭建E2E测试环境
  译-Sinon入门:利用Mocks,Spies和Stubs完成javascript测试
  前两篇说的都是“端到端测试”,这种测试一般都是在项目对应功能很稳定后,才比较适合进行。之前我们团队在推行这种测试时,由于对应功能出现了比较大的变更,导致开发人员的测试脚本失效,很是影响气势。假如项目文档,这种“端到端测试”还是非常有效的,而且也很有意义。
  我们这次要说的是单元测试,应该上面第三篇文章的实战总结篇~~
  单元测试可以在项目初期,甚至没有开始写正式代码之间开始(TDD),测试颗粒度是函数或方法。 你需要把测试目标方法看作是一个黑盒子,根据传递已知的输入参数,来对输出结果进行断言 。单元测试无法代替集成测试和端到端测试。
  很容易,不是么?但对于正式产品而言,单元测试相当重要,毕竟人不可靠(俗话说好记星不如烂笔头,测试脚本是烂笔头)。试想一下,你自己3个月前写的代码你确定还记得么(我的记忆只有3天)?一个1000行项目,某个函数你有胆量在几周后随意更改其输入输出结构?即便是你记忆超群,你能确保别人不修改你的代码么?
  我们如何一次性解决这类问题?单元测试,没错。有了测试脚本,下一步是自动化测试流程(尝试持续集成),这里我们不展开了。接下来我们主要讲javascript项目的单元测试。
  这里用“javascript项目”的目的是:浏览器端项目和nodejs项目都适用。但这并不表示任何javascript代码都可以很容易被单元测试,你依然需要在代码结构上下大功夫,这考验你的代码驾驭能力和代码架构能力。这部分的知识靠的是日积月累,幸运的是大量资料都提供了相当多的佳实践和准则~~
  在这次的项目测试中,Sinon提供了相当大的便利,但是由于第一次使用,也碰到了一些问题,下面列出我在写测试项时的一些收获和采坑:
  1、尽可能使用Sinon提供的断言,因为它携带了更多的错误信息供我们排查;
  2、把方法(或函数)想象成黑盒子,不要试图测试方法体中的内部变量状态,关注返回值;
  3、方法中依赖的第三方资源都要尽可能模拟,不仅是为了避免测试环境复杂,也因为阻断它们的变更而导致的破坏;
  4、没有返回值的方法一般都修改了对应模块(或类)的属性状态,尽可能避免开发时写这种方法(函数式编程理念),测试它们需要提供测试脚本访问这些属性的能力(Get方法或直接定义成public);
  5、测试脚本一定要关注方法输入参数的边缘情况,不要只围绕着正常系走,所有分支都要测试到(盯着方法体代码写测试项,很容易陷入思维困局~~);
  6、异步逻辑的测试,可以使用Sinon的stub,通过检查stub的调用次数、输入参数和callback来完成单元测试(对于callback不依赖传参的方法,参考第4条);
  7、Sinon的Fake timers使用时记住要先创建fake timer,再导入目标方法的定义,否则sinon无法控制目标方法的时间轴(例如setTimeout);
  8、关注测试脚本需要的运行时间,尽可能编写运行效率高的测试项,毕竟测试脚本多数情况下都是交给自动化测试流程运行的,时效性意义很大(每次代码提交都可能会触发执行测试)。