阿里技术专家:持续交付与微服务背后的实践逻辑(2)
把开发的周期分解为一个一个的迭代,比如两周到四周的时间.在迭代开始前,保证该迭代内计划的需求分析完毕.然后在迭代内部开发.顺利的话,会在接近迭代末尾时完成迭代内计划的所有任务,然后拉一个发布分支出来,开始测试,然后发布. 做出上述妥协的直接原因就是“测试和部署”花费的时间过长.如果只花费一个人一个小时就能够完成回归和发布,那显然团队就更愿意去频繁的发布. 一个行之有效的方法就是进行自动化测试. 自动化测试大致可以分为几种:单元测试、API测试、验收测试/功能测试/端到端测试.在不同的技术栈下,分类可能会略有不同的,但本质上来讲是类似的.不同层次的测试有自己的侧重点,组要组合使用来达到一个比较好的效果.如上图所示: 这里以Java Spring项目为例来列举不同层次的测试工具:单元测试使用Junit;集成测试使用Spring Test + Junit;功能测试使用cucumber+ capybara+selinium或者robotframework+ selinium. 如果使用了前端框架,比如Angular、ReactJS等,它们本身也提供了相应的测试框架. 底层的单元测试,测试的范围较小,一般只涉及一个或者几个类,不会调用网络或者数据库.所以编写和运行起来都比较快.在这个级别应该覆盖尽量多的分支和逻辑.这个级别的测试能够达到比较高的覆盖率,所以在它的保护下,可以放心大胆的做重构或者添加新代码,只需要花上几秒钟的时间运行一遍单元测试,就能够知道这次修改是否引入了问题.前阵子做了很长时间的nodejs开发,单元测试对这种弱类型的语言尤其重要.因为像变量未定义,传参数的个数错误等很低级的问题IDE都无法给出有效的提示. 因为单元测试需要隔离被测类和系统的其它代码,所以需要有一些测试替身来代替真实的类.有很多工具可以做这样的事情,比如Java中的mockito. 如上图所示,它会创造一个假的ClassB的实例出来,并传给ClassA的实例.然后对ClassB的行为做出一些假设,在此假设的基础上对ClassA的行为进行测试. 但是一个类或者几个类的正确,并不能让你对系统的正确性有足够的信心.因为单元测试中充满了对其它类行为的假设.所以一旦这个假设错误,就会出现测试依然能通过,但整个系统的行为已经错了的尴尬情况.所以我们还需要覆盖面更大的集成测试.这种测试在服务内部不使用任何的测试替身.但对外部的服务进行打桩.对基于HTTP的服务进行打桩的工具包括moco(https://github.com/dreamhead/moco)和pact(https://github.com/realestate-com-au/pact,其实pact能做的事情不止打桩,更多的是做“契约测试”).这种测试更真实,但运行起来会慢,所以这个层面的测试主要保证的是连通性.不需要100%的覆盖率. 集成测试覆盖面很大,但它仍然是白盒测试,因为它直接调用了函数(比如上页的controller).如果这个服务只提供API,那么这种测试就够了.但如果这个服务是提供页面的,也就是一个web应用,那么就还需要一层直接操作网页来进行基于用户行为的测试,我们一般称之为验收测试,或者功能测试.上面列举的cucumber和robotframework是非常流行的两款功能测试工具.他们是通用的测试框架,与具体的被测系统是无关的.我也另一位前同事都对这两种工具比较熟悉,并且写了文章作总结:http://www.infoq.com/cn/articles/cucumber-robotframework-comparison 如果要测试web系统的话,就需要能够驱动网页的驱动程序.现在非常主流的驱动是selinium(https://github.com/SeleniumHQ/selenium ),当然我更喜欢的是在其上包了一层的capybara(https://github.com/jnicklas/capybara),它是用ruby编写的,封装的API更好用. 建议至少对核心的流程编写功能测试,以保证上线不要出现严重的故障.前段时间我们的功能测试发现了一个bug.这个bug对于老用户都不会有问题,但是用户首次登录就会500.而用户首次登录的场景恰恰是平时自测的时候很容易忽略的,因为准备数据还是有点麻烦的.当时发现这个问题的时候大家并没有什么感觉,因为已经习惯有测试保护的软件了.但如果跳出来想想,这个问题要是在一周后的“迭代末尾”才发现,会多么的打击气势.如果上线才发现,那么产品的新用户增长量一定会直线下降. 端到端测试其实也就是使用功能测试的工具在更大的范围进行测试,也就是包含所有的服务. 下面总结一下各个层次测试的特点. 回归测试是迭代开发中必不可少的一个步骤.我们能做的就是通过自动化测试去尽量减小这个时间. 很多人对于测试有一些顾虑,觉得会花费很多时间.而且当代码结构调整时,测试也要跟着改. 我的看法是,测试代码也是代码,维护测试代码的代价跟测试代码本身的质量是直接相关的.所以对测试代码也需要及时重构,提高可维护和可重用性.具体一点对于测试来说,提高可维护性和看重用性,无非就是要在数据准备、断言工具方面去抽取一些库.比如Ruby的factory girl就是一个极好的基于ActiveRecord的数据准备库.有了它,写测试的代价大大降低.那如果你不用Ruby怎么办,那只好自己实现一个其它语言版本的factory girl喽.我上一个项目用的是nodejs,就写了一个nodejs版本的factory girl. 而单元测试能够给你带来的好处不仅仅是回归这么简单.有了完备的单元测试,你才有信心,有动力去做一些重构.只要测试通过,我就知道我的重构是正确的,你才敢不断的去重构,优化代码,才能使得代码更易维护.所以可以说写测试是保证你代码可维护性的必由之路.不要考虑写不写测试,而是考虑,如何低成本的写测试. 当我开发新功能时候,编写好测试运行一下,就知道功能正确与否,这样就不用把服务器启起来,减小反馈的周期.在这个场景下,它会直接节省你的时间,虽然你写了更多的代码. (编辑:ASP站长网) |