聯(lián)調(diào)之痛
最近站會(huì)時(shí),經(jīng)常聽(tīng)見(jiàn)開(kāi)發(fā)人員說(shuō)某Story已經(jīng)開(kāi)發(fā)完了,等待聯(lián)調(diào),然后就沒(méi)有然后了。對(duì)于聯(lián)調(diào)時(shí)間點(diǎn)近的,開(kāi)發(fā)人員會(huì)等一等,一天的時(shí)間很快就過(guò)去了;時(shí)間點(diǎn)遠(yuǎn)的,開(kāi)發(fā)就領(lǐng)新story了,等另一端開(kāi)發(fā)完,已經(jīng)若干天過(guò)去了。我總覺(jué)得其中有什么問(wèn)題,但是說(shuō)不出來(lái)。上周去QCon聽(tīng)了韓老師的分享,突然發(fā)現(xiàn)這種聯(lián)調(diào)在以前團(tuán)隊(duì)的也是有的。只不過(guò)我們的解決方式不一樣,所以問(wèn)題沒(méi)有這么突出。
產(chǎn)生問(wèn)題的原因很簡(jiǎn)單,現(xiàn)在的軟件開(kāi)發(fā),因?yàn)橄到y(tǒng)復(fù)雜性提升,模塊拆分也很普遍,很難形成一個(gè)團(tuán)隊(duì)做端到端交付,沒(méi)有任何外部依賴。典型場(chǎng)景是模塊A由一個(gè)團(tuán)隊(duì)1開(kāi)發(fā),模塊B由團(tuán)隊(duì)2開(kāi)發(fā)。這時(shí),團(tuán)隊(duì)A的功能開(kāi)發(fā)完成后必須要和團(tuán)隊(duì)B開(kāi)發(fā)的相應(yīng)功能聯(lián)調(diào)。目的是走一個(gè)端到端的流程,確認(rèn)功能正確。
問(wèn)題也來(lái)了,
-
團(tuán)隊(duì)1和團(tuán)隊(duì)2的開(kāi)發(fā)計(jì)劃多是獨(dú)立制定,很難做到同一功能在兩個(gè)模塊上同時(shí)開(kāi)發(fā)完成,之間沒(méi)有任何等待。開(kāi)發(fā)快的要等開(kāi)發(fā)慢的,一方的story雖然開(kāi)發(fā)完了,但不能進(jìn)入測(cè)試,延長(zhǎng)了交付時(shí)間。
-
等測(cè)試發(fā)現(xiàn)bug時(shí),已經(jīng)過(guò)去一兩天甚至更久,開(kāi)發(fā)在做其他功能。回來(lái)解決問(wèn)題,又要上下文切換,再度浪費(fèi)時(shí)間。
-
同時(shí)聯(lián)調(diào)發(fā)生問(wèn)題時(shí),定位問(wèn)題還需要花一番功夫。
之前的項(xiàng)目中我們使用一種契約測(cè)試(Contract Testing)的方法來(lái)解決這個(gè)問(wèn)題。
-
首先,兩個(gè)團(tuán)隊(duì)確定接口具體格式,這組格式就相當(dāng)于一組契約,而后兩個(gè)團(tuán)隊(duì)根據(jù)這個(gè)契約開(kāi)發(fā)各自功能。
-
同時(shí),作為消費(fèi)方的團(tuán)隊(duì)1為這個(gè)接口添加一組自動(dòng)化測(cè)試,確保模塊B的功能符合契約。每次模塊B的代碼改動(dòng)都會(huì)觸發(fā)這組測(cè)試,測(cè)試通過(guò)時(shí)意味著生產(chǎn)者為消費(fèi)者提供了正確的功能,反之不能。這樣模塊B的改動(dòng)肯定不會(huì)影響模塊A,即便有影響,也可以通過(guò)測(cè)試在第一時(shí)間反映出來(lái)。
這種方法,節(jié)省了開(kāi)發(fā)人員的聯(lián)調(diào)時(shí)間,QA直接測(cè)試端到端的功能。同時(shí)遇到問(wèn)題時(shí)也很容易定位問(wèn)題產(chǎn)生根源。
這種測(cè)試與模塊A自身的單元測(cè)試不同,著眼點(diǎn)在確保模塊外部依賴的正確性。有了這層保障,模塊A的單元測(cè)試就可以使用Test Double(Fake、Mock等),構(gòu)造出更關(guān)注于自身邏輯的測(cè)試集,提高測(cè)試的運(yùn)行速度和穩(wěn)定性。
采用這種方法時(shí)有兩個(gè)問(wèn)題需要考慮,
-
誰(shuí)來(lái)寫(xiě)測(cè)試 - 這個(gè)回答相對(duì)簡(jiǎn)單,一定要由消費(fèi)者來(lái)寫(xiě),這樣才能確保測(cè)試代碼和產(chǎn)品代碼采用相同的調(diào)用方式。
-
如何組織測(cè)試 - 有兩種方式可以選擇,放在消費(fèi)者端,每個(gè)消費(fèi)者維護(hù)自己的一套測(cè)試;或放在生產(chǎn)者一端,每個(gè)模塊維護(hù)一套針對(duì)本模塊的測(cè)試。第一種方式更容易引起相關(guān)消費(fèi)者團(tuán)隊(duì)的重視,針對(duì)性更強(qiáng),但會(huì)引入一定的代碼重復(fù),第二種正相反。為了更快更有針對(duì)性的發(fā)現(xiàn)問(wèn)題,我所在的團(tuán)隊(duì)使用了第一種方法。
在寫(xiě)這個(gè)的時(shí)候,順便搜了一下,發(fā)現(xiàn)了老馬的這篇文章IntegrationContractTest,基本思想類似,實(shí)現(xiàn)上稍有不同。最后總結(jié)一下,手工聯(lián)調(diào)這種費(fèi)時(shí)費(fèi)力的工作,能少做就少做,能自動(dòng)化就自動(dòng)化,搞軟件的生命短暫,浪費(fèi)在這上不值。