客户端自动化测试研究
測(cè)試作為質(zhì)量保證極其重要的一環(huán),在移動(dòng)App開(kāi)發(fā)流程中起到非常關(guān)鍵的作用。從開(kāi)發(fā)工程師到測(cè)試工程師,人人都應(yīng)具備良好的測(cè)試意識(shí),將隱患和風(fēng)險(xiǎn)在上線(xiàn)之前找出并解決,可以有效的減少線(xiàn)上事故。
美團(tuán)和大眾點(diǎn)評(píng)App作為美團(tuán)點(diǎn)評(píng)平臺(tái)的主要入口,支持承載著美團(tuán)點(diǎn)評(píng)各大業(yè)務(wù)。其中美團(tuán)點(diǎn)評(píng)境外度假業(yè)務(wù)主要包括了出境游相關(guān)業(yè)務(wù)以及所有的境外城市站,也是美團(tuán)點(diǎn)評(píng)非常看重和大力發(fā)展的業(yè)務(wù)線(xiàn)。為了保證質(zhì)量,需要進(jìn)行各項(xiàng)測(cè)試:冒煙測(cè)試[1]、功能測(cè)試、集成測(cè)試、專(zhuān)項(xiàng)性能測(cè)試,回歸測(cè)試[2]。其中冒煙測(cè)試和回歸測(cè)試大多由開(kāi)發(fā)自己手動(dòng)執(zhí)行,有較大的優(yōu)化空間。一方面,測(cè)試的人力成本較高;另一方面,在之前的測(cè)試過(guò)程中發(fā)生過(guò)漏測(cè)等問(wèn)題,這些問(wèn)題在測(cè)試階段被QA發(fā)現(xiàn),又會(huì)再次返工,費(fèi)時(shí)費(fèi)力。
鑒于這兩部分測(cè)試用例相對(duì)穩(wěn)定,不會(huì)頻繁發(fā)生較大的變化,我們打算將其自動(dòng)化,降低人力成本投入,將測(cè)試結(jié)果報(bào)表化,避免人為疏漏造成的一系列問(wèn)題。
[1]冒煙測(cè)試(smoke testing),就是開(kāi)發(fā)人員在個(gè)人版本的軟件上執(zhí)行目前的冒煙測(cè)試項(xiàng)目,確定新的程序代碼不出故障。冒煙測(cè)試的對(duì)象是每一個(gè)新編譯的需要正式測(cè)試的軟件版本,目的是確認(rèn)軟件基本功能正常,可以進(jìn)行后續(xù)的正式測(cè)試工作。冒煙測(cè)試的執(zhí)行者是版本編譯人員。 [2]回歸測(cè)試是軟件測(cè)試的一種,旨在檢驗(yàn)軟件原有功能在修改后是否保持完整。
目前業(yè)界測(cè)試方案非常多,Android和iOS雙平臺(tái)的方案加起來(lái)大約有十七八種。應(yīng)該如何選擇適合團(tuán)隊(duì)的測(cè)試方案呢?我們主要考慮以下幾個(gè)方面:
- 平臺(tái)支持。
- 穩(wěn)定性。
- 維護(hù)成本。
- 可擴(kuò)展性。
其中維護(hù)成本我們尤為看重。目前團(tuán)隊(duì)的開(kāi)發(fā)和測(cè)試同學(xué)任務(wù)都比較飽和,業(yè)務(wù)處于高速發(fā)展期,沒(méi)法抽出太多的時(shí)間開(kāi)發(fā)/維護(hù)測(cè)試腳本,這就需要在這方面做到在投入較少時(shí)間的前提下不影響自動(dòng)化測(cè)試的結(jié)果產(chǎn)出。常規(guī)的TDD[3]是函數(shù)級(jí)別進(jìn)行測(cè)試驅(qū)動(dòng)開(kāi)發(fā),通常需要在代碼級(jí)別做很多工作,需要測(cè)試團(tuán)隊(duì)投入較大的開(kāi)發(fā)成本。鑒于在成本方面的考慮,我們打算使用BDD[4]來(lái)解決這個(gè)問(wèn)題。主要在行為層面進(jìn)行測(cè)試投入,在代碼層級(jí)方面投入較小,用非常有辨識(shí)力的行為進(jìn)行測(cè)試。
在平臺(tái)支持方面,由于是客戶(hù)端團(tuán)隊(duì),所以我們希望寫(xiě)好的用例可以同時(shí)跑在Android和iOS兩個(gè)平臺(tái)上,還希望用例可以一部分進(jìn)行美團(tuán)和大眾點(diǎn)評(píng)兩個(gè)App的復(fù)用,所以需要一個(gè)可以跨平臺(tái)的方案。
[3]測(cè)試驅(qū)動(dòng)開(kāi)發(fā)(Test-driven development,縮寫(xiě)為T(mén)DD)是一種軟件開(kāi)發(fā)過(guò)程中的應(yīng)用方法,倡導(dǎo)先寫(xiě)測(cè)試程序,然后編碼實(shí)現(xiàn)其功能得名。測(cè)試驅(qū)動(dòng)開(kāi)發(fā)是戴兩頂帽子思考的開(kāi)發(fā)方式:先戴上實(shí)現(xiàn)功能的帽子,在測(cè)試的輔助下,快速實(shí)現(xiàn)其功能;再戴上重構(gòu)的帽子,在測(cè)試的保護(hù)下,通過(guò)去除冗余的代碼,提高代碼質(zhì)量。 [4]行為驅(qū)動(dòng)開(kāi)發(fā)(Behavior-driven development,縮寫(xiě)B(tài)DD)是一種敏捷軟件開(kāi)發(fā)的技術(shù)。它通過(guò)用自然語(yǔ)言書(shū)寫(xiě)非程序員可讀的測(cè)試用例擴(kuò)展了測(cè)試驅(qū)動(dòng)開(kāi)發(fā)方法。
去年年底的時(shí)候我們團(tuán)隊(duì)就自動(dòng)化測(cè)試方面進(jìn)行了探索。發(fā)現(xiàn)Calabash滿(mǎn)足BDD和跨平臺(tái),于是進(jìn)行了小范圍試用。在腳本開(kāi)發(fā)和維護(hù)方面,成本確實(shí)低于函數(shù)級(jí)別的測(cè)試開(kāi)發(fā),它可以用一種類(lèi)似自然語(yǔ)言的方式編寫(xiě)測(cè)試用例,這是一個(gè)簡(jiǎn)單的test case示例:
Scenario: 首頁(yè)Then I press "上海"When I press view with id "city"Then I see "海外"When I press "海外"And I press view with id "start_search"When I enter "東京" into input field number 1Then I press list item number 1Then I see "東京"When I press "美食"...這個(gè)示例相信開(kāi)發(fā)工程師們甚至沒(méi)寫(xiě)過(guò)代碼的人也看得懂,其實(shí)就是用常規(guī)的行為思維模式去編寫(xiě)測(cè)試用例。其中Feature、Scenario、Step是BDD的三個(gè)核心概念:
- Feature:就是字面意思,主要是描述功能特性。
- Scenario:場(chǎng)景,在這里可以簡(jiǎn)單的理解為一個(gè)個(gè)的細(xì)分case,通常情況下需要多個(gè)場(chǎng)景拼接來(lái)完成一個(gè)具體的test case。
- Step:實(shí)現(xiàn)場(chǎng)景的步驟代碼。
但是Calabash在業(yè)內(nèi)相對(duì)小眾,遇到問(wèn)題就不太好解決。比如在某些三星手機(jī)上就遇到了某些控件根據(jù)ID找不到的問(wèn)題,會(huì)影響UI元素的定位。在編寫(xiě)自動(dòng)化腳本時(shí),元素定位的唯一性是一個(gè)看似簡(jiǎn)單實(shí)際上會(huì)有很多坑的問(wèn)題,腳本的穩(wěn)定性一定程度上依賴(lài)了如何進(jìn)行元素定位。
其次,在Android團(tuán)隊(duì)想要把方案推廣到iOS平臺(tái)的時(shí)候,我們發(fā)現(xiàn)了一個(gè)很大的問(wèn)題:iOS接入Calabash的成本太高。Android的接入成本很低,只需要一個(gè)重簽名的apk文件就可以了,并不依賴(lài)源碼,而iOS的接入需要依賴(lài)源碼做一些工作,這就給iOS同學(xué)造成了很多困難。美團(tuán)和大眾點(diǎn)評(píng)是兩個(gè)巨大的App,在源碼接入方面的工作量并不小,而且很多隱患無(wú)法預(yù)料,就算依賴(lài)源碼接入之后,還有一個(gè)問(wèn)題需要解決:iOS的ID系統(tǒng)。通常iOS業(yè)務(wù)開(kāi)發(fā)代碼中不是通過(guò)ID來(lái)獲取頁(yè)面元素,不管是手寫(xiě)布局代碼還是用xib布局,開(kāi)發(fā)者一般不會(huì)給界面元素加ID,所以iOS的元素大多都沒(méi)有ID,而Calabash對(duì)元素的定位主要依賴(lài)ID,這無(wú)疑讓我們感到雪上加霜。
在Android團(tuán)隊(duì)用寫(xiě)好的用例進(jìn)行了幾個(gè)版本的冒煙測(cè)試之后,團(tuán)隊(duì)內(nèi)部Android、iOS、QA的同學(xué)坐下來(lái)一起進(jìn)行了方案后續(xù)的探究,最終決定放棄Calabash,繼續(xù)尋找可以替代的方案。
在經(jīng)歷過(guò)Calabash的挫折之后,我們?cè)谶x型方面更加慎重。QA同學(xué)對(duì)Appium有一定的經(jīng)驗(yàn),于是先采用了Appium方案進(jìn)行兼容性測(cè)試和部分回歸測(cè)試。在業(yè)務(wù)快速發(fā)展的過(guò)程中,維護(hù)成本讓QA同學(xué)越來(lái)越疲于應(yīng)付,于是我們又坐在一起進(jìn)行新方案的討論和探索。
Calabash的BDD模式是大家認(rèn)可的,也是大家愿意接受的,那就需要在新的方案中,繼續(xù)使用這種方式編寫(xiě)維護(hù)測(cè)試用例。我們想把Appium和Calabash兩者的優(yōu)勢(shì)結(jié)合起來(lái),還想把之前寫(xiě)過(guò)的Calabash的測(cè)試用例無(wú)縫遷移繼續(xù)使用。
取其精華
Calabash為什么可以使用類(lèi)似自然語(yǔ)言的方式編寫(xiě)測(cè)試用例達(dá)到BDD的效果呢?根本原因是因?yàn)镃ucumber。
在Calabash官網(wǎng)中注明了他們使用了Cucumber(一種簡(jiǎn)單的自然語(yǔ)言方式的BDD開(kāi)源解決方案),那么我們能否底層使用Appium支持,上層使用Cucumber進(jìn)行測(cè)試用例的開(kāi)發(fā)和維護(hù)呢?
答案當(dāng)然是可行的。我們?cè)贏ppium的官方示例代碼中找到了答案。Appium官方提供了與Cucumber結(jié)合使用的例子作為參考,雖然這部分代碼已經(jīng)兩年沒(méi)更新了,但是依然給我們提供了關(guān)鍵思路。
新方案形成
客戶(hù)端的同學(xué)與QA同學(xué)進(jìn)行了討論,確認(rèn)了使用QA同學(xué)目前使用的按照App進(jìn)行用例拆分的方案。之前Calabash的方案有很多可以借鑒過(guò)來(lái),于是我們先進(jìn)行了整體結(jié)構(gòu)的調(diào)整:
按照點(diǎn)評(píng)和美團(tuán)兩個(gè)App進(jìn)行用例區(qū)分,公共步驟的封裝在common_steps.rb中。點(diǎn)評(píng)和美團(tuán)的目錄下分別有cucumber.yml腳本,這是用來(lái)區(qū)分Android和iOS平臺(tái)的,內(nèi)容大概是這樣:
# config/cucumber.yml ##YAML Template --- ios: IDEVICENAME='ios' android: IDEVICENAME='android'其中Android/config和iOS/config是Android和iOS兩個(gè)平臺(tái)的特定配置,這部分配置代碼在support包內(nèi),是Appium啟動(dòng)需要加載的配置。
平臺(tái)的區(qū)分在env.rb中體現(xiàn)出來(lái):
class AppiumWorld endif ENV['IDEVICENAME']=='android'caps = Appium.load_appium_txt file: File.expand_path("./../android/appium.txt", __FILE__), verbose: true elsif ENV['IDEVICENAME']=='ios'caps = Appium.load_appium_txt file: File.expand_path("./../ios/appium.txt", __FILE__), verbose: true elsecaps = Appium.load_appium_txt file: File.expand_path('./', __FILE__), verbose: true end Appium::Driver.new(caps) Appium.promote_appium_methods AppiumWorldWorld doAppiumWorld.new end這樣通過(guò)cucumber -p android/ios就能運(yùn)行相應(yīng)平臺(tái)的用例了,Cucumber其他參數(shù)自行查閱,和Calabash非常相似。
完全移除Calabash之后,所有Calabash內(nèi)置的Steps就沒(méi)有了,需要重新封裝。其中Feature、Scenario、Step的概念沒(méi)有發(fā)生變化,和Calabash完全一致。重新封裝Steps需要依賴(lài)appium_lib。為了降低封裝成本,提供更多可用的Steps,我們還引入了selenium-cucumber作為輔助使用。
最后testdata.rb是保存測(cè)試數(shù)據(jù)的文件,例如測(cè)試賬號(hào)的登錄用戶(hù)名和密碼等數(shù)據(jù)。
最終需要依賴(lài)的庫(kù)大致是這些:
gem 'appium_lib', '~> 9.4.2' gem 'rest-client', '~> 2.0.2' gem 'rspec', '~> 3.5.0' gem 'cucumber', '~> 2.4.0' gem 'rspec-expectations', '~> 3.5.0' gem 'spec', '~> 5.3.4' gem 'selenium-cucumber', '~> 3.1.5'這樣就完成了組合方案的整體框架。
新方案形成之后,我們的提測(cè)流程就多了一道保障:
于是每個(gè)客戶(hù)端RD都可以愉快的點(diǎn)擊腳本生成測(cè)試報(bào)告,提交給QA同學(xué),省去了大家本地跑測(cè)試的時(shí)間,也幫助QA同學(xué)節(jié)約了時(shí)間,不會(huì)再出現(xiàn)返工或者測(cè)試遺漏的情況。
整體穩(wěn)定性提高
由于底層切換到了Appium,穩(wěn)定性提高了,同樣的機(jī)型不再出現(xiàn)類(lèi)似Calabash的不兼容問(wèn)題了(根據(jù)ID無(wú)法定位到某個(gè)元素),QA同學(xué)在Appium的自動(dòng)化道路上已經(jīng)做過(guò)不少實(shí)踐,具有相關(guān)經(jīng)驗(yàn)。在Webview方面支持也是比較好的,相比Calabash只是多了切換Webview和Native上下文的步驟,Appium的優(yōu)勢(shì)完全體現(xiàn)出來(lái)了。
iOS接入成本降低
針對(duì)Android和iOS的接入成本,也降低到了一致。Android依舊是提供apk,iOS提供重簽名的ipa包即可,無(wú)需源碼集成,這就解決了Calabash方案iOS集成成本大的問(wèn)題。
元素定位手段增多
公共Steps一次封裝處處可用,在跨App復(fù)用的業(yè)務(wù)上,測(cè)試代碼也幾乎可以復(fù)用,編寫(xiě)測(cè)試腳本的成本再次降低。iOS控件缺少I(mǎi)D不好定位的問(wèn)題也得到了解決,Appium支持ID、class、name、XPath等元素定位方式,如果前三者都不可用的情況下,使用相對(duì)復(fù)雜但幾乎萬(wàn)能的XPath都可以得到解決。
例如一個(gè)復(fù)雜的XPath:
Then I press view with xpath "//android.widget.LinearLayout[1] /android.widget.FrameLayout[1]/android.widget.LinearLayout[1] /android.widget.FrameLayout[1]/android.widget.LinearLayout[2] /android.widget.FrameLayout[1]/android.widget.ListView[1] /android.widget.LinearLayout[1]/android.widget.LinearLayout[1]"不用擔(dān)心這么復(fù)雜的XPath應(yīng)該怎么寫(xiě),這其實(shí)是最簡(jiǎn)單的,因?yàn)榭梢酝ㄟ^(guò)Appium-inspector抓取得到。當(dāng)然XPath的寫(xiě)法有很多種,可以選用兼容性更好的寫(xiě)法。
原有腳本無(wú)縫遷移
之前在使用Calabash的時(shí)候編寫(xiě)的腳本,在封裝好公共Steps之后,幾乎無(wú)縫的進(jìn)行了遷移,對(duì)上層編寫(xiě)測(cè)試用例的同學(xué)來(lái)說(shuō),幾乎沒(méi)有變化,無(wú)需關(guān)心是Calabash還是Appium,使用和原先一樣的BDD方式繼續(xù)愉快的寫(xiě)用例就好。
Calabash方案時(shí)期的homepage場(chǎng)景(部分):
切換新方案后homepage場(chǎng)景(部分):
并沒(méi)有太大的差別。
易集成Jenkins,報(bào)告可視化
Cucumber可以進(jìn)行報(bào)表的可視化輸出,只要在命令后面追加--format html --out reports.html --format pretty,在執(zhí)行完全部腳本之后就可以看到生成好的HTML格式的測(cè)試報(bào)告,也可以使用JSON的格式。
集成Jenkins的方式也相對(duì)常規(guī),只要安裝好需要的依賴(lài)就可以。
在測(cè)試過(guò)程中,我們使用了公司內(nèi)部的云測(cè)機(jī)器遠(yuǎn)程平臺(tái):
利用遠(yuǎn)程平臺(tái)的真機(jī)進(jìn)行遠(yuǎn)程腳本測(cè)試,測(cè)試報(bào)告示例如下:
在境外業(yè)務(wù)線(xiàn)客戶(hù)端進(jìn)行了自動(dòng)化測(cè)試實(shí)踐,目前用于固有冒煙自動(dòng)化,方案前后對(duì)比如下。
Calabash方案時(shí)期境外點(diǎn)評(píng)固有冒煙用例耗時(shí):
新方案境外點(diǎn)評(píng)固有冒煙用例耗時(shí)(相比之前Calabash方案時(shí)期的用例有所增加):
通過(guò)數(shù)據(jù)對(duì)比可以看出,用例數(shù)量與執(zhí)行耗時(shí)并不是嚴(yán)格的線(xiàn)性關(guān)系,在用例數(shù)量擴(kuò)大一倍的情況下,耗時(shí)并不會(huì)線(xiàn)性的擴(kuò)大一倍。
開(kāi)發(fā)成本:單個(gè)用例的開(kāi)發(fā)成本主要根據(jù)用例規(guī)模相關(guān),開(kāi)發(fā)一個(gè)包含7個(gè)動(dòng)作的用例大概耗時(shí)30分鐘左右,其中包括了定位元素的耗時(shí)。多個(gè)用例的開(kāi)發(fā)成本不止和用例規(guī)模相關(guān),還和用例之間是否有復(fù)用的場(chǎng)景相關(guān),這就牽扯到了Scenario拆分粒度的問(wèn)題,下文中有提到。
目前執(zhí)行用例美團(tuán)+點(diǎn)評(píng)總耗時(shí)20分鐘左右,降低了人力成本,避免了QA同學(xué)返工的情況,方案新老交替無(wú)縫平滑過(guò)渡,維護(hù)成本低。這不僅是我們團(tuán)隊(duì)對(duì)自動(dòng)化方案的期許,也是自動(dòng)化測(cè)試的價(jià)值所在。
問(wèn)題
scroll or swipe?
在使用UIAutomation的時(shí)候,Android頁(yè)面滑動(dòng)采取的方式是調(diào)用scroll_uiselector方法,例如:
Then /^I scroll to view with text "([^\"]*)"$/ do |value|text = %Q("#{value}")args = scroll_uiselector("new UiSelector().textContains(#{text})")find_element :uiautomator, args end但是這種方式存在不穩(wěn)定性因素,在某些情況下,滑動(dòng)搜索UI元素非常慢(上下滑動(dòng)很多次)甚至滑動(dòng)多次最后仍然搜索不到,腳本會(huì)執(zhí)行失敗。在比較復(fù)雜的App上很容易出現(xiàn),是整體腳本穩(wěn)定性和成功率的瓶頸。如果更換為UIAutomation2,就可以使用swipe語(yǔ)句進(jìn)行相對(duì)精準(zhǔn)的滑動(dòng):
swipe start_x: start_x, start_y: start_y, end_x: start_x, end_y: start_y - pixel.to_i根據(jù)撰寫(xiě)本文時(shí)Appium的最新版本v1.6.5進(jìn)行實(shí)踐,發(fā)現(xiàn)切換UIAutomation2后使用swipe滑動(dòng),對(duì)比scroll的方式成功率提高了一倍多,耗時(shí)減半,效果非常顯著。雖然其他語(yǔ)句會(huì)略微受一點(diǎn)影響,不過(guò)整體改動(dòng)幅度很小,性?xún)r(jià)比很高,而且UIAutomation2還支持對(duì)Toast的識(shí)別,整體穩(wěn)定性大幅提高,建議使用UIAutomation2。
Scenario拆分粒度
在很多情況下,一個(gè)test case是由一個(gè)或多個(gè)Scenario組成的,不同的test case又會(huì)存在部分Scenario復(fù)用的情況,明確Scenario的拆分粒度可以幫助開(kāi)發(fā)人員降低測(cè)試腳本的編寫(xiě)成本,達(dá)到一定程度上的App內(nèi)部復(fù)用甚至跨App復(fù)用。尤其在多人協(xié)作的環(huán)境下,這是一個(gè)非常值得探究的問(wèn)題。
展望
自動(dòng)觸發(fā)云測(cè)
目前觸發(fā)的方式是人工觸發(fā)Jenkins job,最后輸出報(bào)告。未來(lái)要做的是在特定的時(shí)期自動(dòng)觸發(fā)job進(jìn)行云端自動(dòng)化,觸發(fā)時(shí)期可能會(huì)參考App的開(kāi)發(fā)周期時(shí)間節(jié)點(diǎn)。
人人都是測(cè)試工程師
我們希望團(tuán)隊(duì)內(nèi)人人都具備良好的測(cè)試思維,能站在測(cè)試的角度想問(wèn)題,領(lǐng)悟測(cè)試驅(qū)動(dòng)開(kāi)發(fā)的意義。通過(guò)簡(jiǎn)單的方式讓團(tuán)隊(duì)內(nèi)的同學(xué)們參與測(cè)試,體會(huì)測(cè)試,寫(xiě)出更優(yōu)秀的代碼。
立成,美團(tuán)點(diǎn)評(píng)酒旅境外度假研發(fā)組Android高級(jí)開(kāi)發(fā)工程師,在Android開(kāi)發(fā)、跨平臺(tái)開(kāi)發(fā)、移動(dòng)端測(cè)試等領(lǐng)域有一定的實(shí)踐經(jīng)驗(yàn),熱愛(ài)新技術(shù)并愿意付諸實(shí)踐,致力于產(chǎn)出高質(zhì)量代碼。
酒旅境外度假研發(fā)組,負(fù)責(zé)美團(tuán)點(diǎn)評(píng)境外度假業(yè)務(wù)。美團(tuán)點(diǎn)評(píng)海外站覆蓋全球100多個(gè)國(guó)家地區(qū),收錄美食、購(gòu)物、酒店、景點(diǎn)等各類(lèi)海外商戶(hù)超過(guò)500萬(wàn)家,為出國(guó)游玩用戶(hù)提供吃、玩、住、買(mǎi)、交通一站式解決方案。我們的使命是做「最好用的境外中文消費(fèi)指南」,全方位解決用戶(hù)海外旅行中的問(wèn)題,最終成為出境游行業(yè)技術(shù)領(lǐng)域智能化、國(guó)際化的標(biāo)桿!我們團(tuán)隊(duì)長(zhǎng)期招聘Android、iOS、FE、Java、算法等技術(shù)方向的工程師,誠(chéng)摯歡迎投遞簡(jiǎn)歷至hongguangyan#meituan.com。
【思考題】 本文為大家介紹的新型客戶(hù)端自動(dòng)化測(cè)試方案大大降低了自動(dòng)化測(cè)試的成本,但是依然存在測(cè)試腳本的迭代與維護(hù)的問(wèn)題。那么應(yīng)該在什么時(shí)間節(jié)點(diǎn)或者時(shí)間段對(duì)腳本進(jìn)行維護(hù),才能保證在測(cè)試過(guò)程中不會(huì)因?yàn)槟_本滯后而導(dǎo)致測(cè)試失敗?如何將這部分成本降到最低?如何實(shí)現(xiàn)人人都是測(cè)試工程師的愿景?
總結(jié)
以上是生活随笔為你收集整理的客户端自动化测试研究的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 美团外卖客户端高可用建设体系
- 下一篇: 最全多线程经典面试题和答案