最近站会时,经常听见开发人员说某Story已经开发完了,等待联调,然后就没有然后了。对于联调时间点近的,开发人员会等一等,一天的时间很快就过去了;时间点远的,开发就领新story了,等另一端开发完,已经若干天过去了。我总觉得其中有什么问题,但是说不出来。上周去QCon听了韩老师的分享,突然发现这种联调在以前团队的也是有的。只不过我们的解决方式不一样,所以问题没有这么突出。
产生问题的原因很简单,现在的软件开发,因为系统复杂性提升,模块拆分也很普遍,很难形成一个团队做端到端交付,没有任何外部依赖。典型场景是模块A由一个团队1开发,模块B由团队2开发。这时,团队A的功能开发完成后必须要和团队B开发的相应功能联调。目的是走一个端到端的流程,确认功能正确。
问题也来了,
-
团队1和团队2的开发计划多是独立制定,很难做到同一功能在两个模块上同时开发完成,之间没有任何等待。开发快的要等开发慢的,一方的story虽然开发完了,但不能进入测试,延长了交付时间。
-
等测试发现bug时,已经过去一两天甚至更久,开发在做其他功能。回来解决问题,又要上下文切换,再度浪费时间。
-
同时联调发生问题时,定位问题还需要花一番功夫。
之前的项目中我们使用一种契约测试(Contract Testing)的方法来解决这个问题。
-
首先,两个团队确定接口具体格式,这组格式就相当于一组契约,而后两个团队根据这个契约开发各自功能。
-
同时,作为消费方的团队1为这个接口添加一组自动化测试,确保模块B的功能符合契约。每次模块B的代码改动都会触发这组测试,测试通过时意味着生产者为消费者提供了正确的功能,反之不能。这样模块B的改动肯定不会影响模块A,即便有影响,也可以通过测试在第一时间反映出来。
这种方法,节省了开发人员的联调时间,QA直接测试端到端的功能。同时遇到问题时也很容易定位问题产生根源。
这种测试与模块A自身的单元测试不同,着眼点在确保模块外部依赖的正确性。有了这层保障,模块A的单元测试就可以使用Test Double(Fake、Mock等),构造出更关注于自身逻辑的测试集,提高测试的运行速度和稳定性。
采用这种方法时有两个问题需要考虑,
-
谁来写测试 - 这个回答相对简单,一定要由消费者来写,这样才能确保测试代码和产品代码采用相同的调用方式。
-
如何组织测试 - 有两种方式可以选择,放在消费者端,每个消费者维护自己的一套测试;或放在生产者一端,每个模块维护一套针对本模块的测试。第一种方式更容易引起相关消费者团队的重视,针对性更强,但会引入一定的代码重复,第二种正相反。为了更快更有针对性的发现问题,我所在的团队使用了第一种方法。
在写这个的时候,顺便搜了一下,发现了老马的这篇文章IntegrationContractTest,基本思想类似,实现上稍有不同。最后总结一下,手工联调这种费时费力的工作,能少做就少做,能自动化就自动化,搞软件的生命短暂,浪费在这上不值。