導航:首頁 > 操作系統 > androiduitest

androiduitest

發布時間:2024-06-22 04:20:21

A. 鎬庢牱浣跨敤Appium榪涜孉ndroid鑷鍔ㄥ寲嫻嬭瘯

1銆丷obotium鈥斺斿畨鍗撴祴璇曞伐鍏 Robotium鏄瀹夊崜緋葷粺鏈甯哥敤鐨勮嚜鍔ㄥ寲嫻嬭瘯宸ュ叿錛屽苟涓旀槸涓嬈懼厤璐圭殑瀹夊崜UI嫻嬭瘯宸ュ叿銆傚畠閫傚悎浜庡悇縐嶄笉鍚岀殑瀹夊崜鐗堟湰鍙婂叾涓嬭岀増鏈銆傝蔣浠跺紑鍙戣呯粡甯告妸瀹冪О浣滃畨鍗撱俁obotium鍒涘緩鐨勬祴璇曚嬌鐢↗ava鍐欑殑銆備簨瀹炰笂錛孯obotium鏄涓涓涓浣撴祴璇曟暟鎹搴撱 浣嗘槸Robotium闇瑕佽姳璐瑰緢闀挎椂闂村姫鍔涘幓鍒涘緩嫻嬭瘯錛屽氨鍍忎負浜嗚嚜鍔ㄥ寲紼嬪簭鍒涘緩鐨勬簮浠g爜銆傚畠涓嶉傚悎浜掑姩鐨勮蔣浠剁郴緇燂紝涓嶈兘閿佷綇鍜岃В閿佹櫤鑳芥墜鏈恆俁obotium娌℃湁璁板綍鍜屾挱鏀懼姛鑳斤紝瀹冧笉鏀鎸佹埅灞忋 2銆丮onkeyRunner鈥斺斿畨鍗揂pp嫻嬭瘯宸ュ叿 MonkeyRunner鏄鏈嫻佽岀殑鏈夎嚜鍔ㄥ寲鍔熻兘鐨勫畨鍗撹蔣浠舵祴璇曞伐鍏楓侻onkeyRunner姣旇搗Robotium瑕佷綆絝涓浜涖傚畠騫朵笉澶勭悊婧愪唬鐮併傛祴璇曞壋寤烘槸鐢≒ython鍐欑殑錛屽叾涓鍙鑳戒嬌鐢ㄨ板綍宸ュ叿錛屼負浜嗗壋寤烘祴璇曘侻onkeyRunner鍙浠ュ湪榪炴帴鐘舵佺殑PC鎴栬呮ā鎷熷櫒涓婅繍琛屾祴璇曘傚畠鏈変竴涓搴旂敤紼嬪簭鎺ュ彛鍙浠ユ帶鍒舵櫤鑳芥墜鏈烘垨鑰呮ā鎷熷櫒銆備絾鎵嬫満APP嫻嬭瘯宸ュ叿鐨勬渶澶х己闄鋒槸姣忎釜璁懼囬兘瑕佺紪鍐欒剼鏈銆傚彟涓涓緙洪櫡灝辨槸錛屾瘡嬈℃祴璇曠▼搴忓彂鐢熸敼鍙樻椂閮借佽皟鏁淬 3銆丷anorex鈥斺斿畨鍗揂pp嫻嬭瘯宸ュ叿 Ranorex鏄涓涓涓嶉敊鐨勮嚜鍔ㄥ寲嫻嬭瘯宸ュ叿錛屼笉浠呮渶鏂扮増鏈錛孉ndroid 2.2.浠ヤ笂鐗堟湰閮芥槸鍙浠ョ殑銆俁anorex鐨勫ソ澶勫湪浜庡畠鏈夎︾粏鐨勬埅灞忔姤鍛娿備粬鍙浠ラ氳繃WiFi涓婄綉榪炴帴鏅鴻兘鎵嬫満鎴栬呭鉤鏉跨數鑴戙傞氳繃榪欎釜 android 宸ュ叿錛岃嚜鍔ㄥ寲鐨勬祴璇曞伐紼嬪笀鍙浠ヨ︾粏鎻忚堪鏁版嵁椹卞姩嫻嬭瘯錛屼絾涓嶅寘鎷 XML 鏁版嵁鏍煎紡銆俁anorex鍙浠ュ緢杞繪澗鍦板壋寤烘祴璇曪紝鑷鍔ㄥ寲嫻嬭瘯宸ョ▼甯堝彧闇鐐瑰嚮榧犳爣銆俁anorex鍏佽擱檮鍔犵殑紼嬪簭妯″潡銆傝繖涓妯″潡鍙浠ヨ鐢ㄤ簬寮鍙戞洿涓哄嶆潅鐨勬祴璇曞満鏅涓銆俁anorex鏄涓涓鍟嗕笟鍖栫殑縐誨姩搴旂敤紼嬪簭鐨勫伐鍏;鍏惰稿彲浠鋒牸鏄 1990嬈у厓/騫淬俁anorex鎼滅儲鐩稿綋鎱;瀹冮渶瑕 30 縐掔殑鏃墮棿鏉ユ墽琛屾搷浣溿傚叾涓涓涓蹇呴』涓篟anorex鏂囦功鐨 APK 鏂囦歡鎷嗚愩傚惁鍒欏畠涓嶈兘閫氳繃榪欎釜宸ュ叿榪涜岃嚜鍔ㄥ寲嫻嬭瘯錛屽畠鍙鑳藉湪APK 鏂囦歡涓嬪伐浣溿 4銆丄ppium鈥斺斿畨鍗撹嚜鍔ㄥ寲嫻嬭瘯宸ュ叿 Appium鏄涓篿OS鍜屽畨鍗撶郴緇熷壋寤虹殑鑷鍔ㄥ寲嫻嬭瘯妗嗘灦錛屾槸涓涓鍏嶈垂宸ュ叿銆傚畠鏀鎸 2.3 鍙婃洿楂樼増鏈鐨 Android 緋葷粺銆侫ppium鍒╃敤WebDriver鐣岄潰榪愯屾祴璇曘傚畠鏀鎸佽稿氱紪紼嬭璦錛屽 java銆 C#銆丷uby鍜屽叾浠栫殑WebDriver鏁版嵁搴撱傚畠鍙浠ュ湪縐誨姩璁懼囦笂鎺у埗 Safari 鍜孋hrome銆備絾鏄錛屼竴浜涜嚜鍔ㄥ寲鐨勬祴璇曞伐紼嬪笀鎶辨ㄥ畠鎻愪緵鐨勬姤鍛婁笉瓚熾傚畠鐨勭己鐐逛篃鍑忓皯浜嗗逛簬XPath鍦ㄧЩ鍔ㄨ懼囦笂鐨勬敮鎸併 5銆乁I Automator鈥斺斿畨鍗撹嚜鍔ㄥ寲嫻嬭瘯 璋鋒瓕鏈榪戞帹鍑轟簡榪欎竴宸ュ叿銆傚畠鏀鎸佷粠4.1寮濮嬬殑瀹夊崜鐗堟湰銆傛垜浠搴旇ラ夋嫨鍙︿竴涓鏇存棭鏈熺殑瀹夊崜搴旂敤紼嬪簭榪涜岃嚜鍔ㄥ寲嫻嬭瘯銆俇I Automator鑳藉熶笌鍚勭被瀹夊崜緋葷粺鍏煎癸紝鍖呮嫭緋葷粺鐨勫簲鐢ㄧ▼搴忋傝繖浣垮緱鏃佹瘉UI Automator鍙浠ラ攣瀹氬拰瑙i攣鏅鴻兘鎵嬫満鎴栧鉤鏉跨數鑴戙傞氳繃璇ュ伐鍏峰壋寤虹殑鑴氭湰鍙浠ュ湪璁稿氫笉鍚岀殑瀹夊崜騫沖彴涓婃墽琛屻傚畠鍏佽稿嶅埗鐢ㄦ埛鐨勬搷浣滃嶆潅鐨勫簭鍒椼俇I Automator涔熷彲浠ュ埄鏃呭惎鏄ョ敤澶栭儴鎸夐挳鐨勮呯疆璋冭妭錛屾墦寮鍜屽叧闂璁懼囩殑鎸夐挳銆 UI Automator鍙浠ヤ笌嫻嬭瘯妗嗘灦TestNG闆嗘垚銆傚湪榪欑嶆儏鍐典笅錛岀敤鎴風晫闈㈣嚜鍔ㄥ彲浠ョ敓鎴愬唴瀹逛赴瀵屽拰璇︾粏鐨勬姤鍛婏紝綾諱技浜庣敱Ranorex鐢熸垚鐨勬姤鍛娿傛ゅ伐鍏鋒悳緔㈤熷害榪橀潪甯稿揩銆傚湪璁稿氬畨鍗撳鉤鍙頒笂嫻嬭瘯鍚庯紝杞浠舵祴璇曚笓瀹惰や負UI Automator鏄璐ㄩ噺鏈濂界殑縐誨姩搴旂敤紼嬪簭銆傚畠鏄瀹夊崜鍋氬ソ鐨勫簲鐢ㄧ▼搴忎箣涓錛屽畠鐢辮胺姝屾帹鍑恆 閫氬父澶х害 80%鐨勬柊杞浠剁殑 bug 閮戒細閲嶇幇鏀鎸佺殑騫沖彴銆傚叾浣 20%鍑虹幇鍦ㄥ叾浠栧鉤鍙頒笂銆傝繖鎰忓懗鐫錛屽湪澶у氭暟鎯呭喌涓嬶紝浜嬪厛嫻嬭瘯杞浠朵駭鍝佹瘮鐩茬洰浣跨敤鏇村ソ銆 鐩鍓嶏紝 Android 4.1 鐗堟湰瀹夎呬簡綰 66%鎿嶄綔緋葷粺鐨勮懼囥傝繖灝辨槸涓轟粈涔堣稿氳嚜鍔ㄥ寲鐨勬祴璇曞伐紼嬪笀緇忓父鍐沖畾UI Automator鏄鏈鍚堥傜殑瑙e喅鏂規堛

B. android app自動化測試工具有哪些

1、Monkey是AndroidSDK自帶的測試工具,在測試過程中會向系統發送偽隨機的用戶事件流,如按鍵輸入、觸摸屏輸入、手勢輸入等),實現對正在開發的應用程序進行壓力測試,也有日誌輸出。實際上該工具只能做程序做一些壓力測試,由於測試事件和數據都是隨機的,不能自定義,所以有很大的局限性。2、MonkeyRunner也是AndroidSDK提供的測試工具。嚴格意義上來說MonkeyRunner其實是一個Api工具包,比Monkey強大,可以編寫測試腳本來自定義數據、事件。缺點是腳本用python來寫,對測試人員來說要求較高,有比較大的學習成本。3、Instrumentation是早期Google提供的Android自動化測試工具類,雖然在那時候JUnit也可以對Android進行測試,但是Instrumentation允許你對應用程序做更為復雜的測試,甚至是框架層面的。通過Instrumentation你可以模擬按鍵按下、抬起、屏幕點擊、滾動等事件。Instrumentation是通過將主程序和測試程序運行在同一個進程來實現這些功能,你可以把Instrumentation看成一個類似Activity或者Service並且不帶界面的組件,在程序運行期間監控你的主程序。缺點是對測試人員來說編寫代碼能力要求較高,需要對Android相關知識有一定了解,還需要配置AndroidManifest.xml文件,不能跨多個App。4、UiAutomator也是Android提供的自動化測試框架,基本上支持所有的Android事件操作,對比Instrumentation它不需要測試人員了解代碼實現細節(可以用UiAutomatorviewer抓去App頁面上的控制項屬性而不看源碼)。基於Java,測試代碼結構簡單、編寫容易、學習成本,一次編譯,所有設備或模擬器都能運行測試,能跨App(比如:很多App有選擇相冊、打開相機拍照,這就是跨App測試)。缺點是只支持SDK16(Android4.1)及以上,不支持HybirdApp、WebApp。5、Espresso是Google的開源自動化測試框架。相對於Robotium和UIAutomator,它的特點是規模更小、更簡潔,API更加精確,編寫測試代碼簡單,容易快速上手。因為是基於Instrumentation的,所以不能跨App。配合AndroidStudio來編寫測試的簡單例子6、Selendroid:也是基於Instrumentation的測試框架,可以測試NativeApp、HybirdApp、WebApp,但是網上資料較少,社區活躍度也不大。7、Robotium也是基於Instrumentation的測試框架,目前國內外用的比較多,資料比較多,社區也比較活躍。缺點是對測試人員來說要有一定的Java基礎,了解Android基本組件,不能跨App。8、Athrun是淘寶出的一個移動測試框架/平台,同時支持iOS和Android。Android部分也是基於Instrumentation,在Android原有的類基礎上進行了擴展,提供一整套面向對象的API。這里有詳細介紹。9、Appium是最近比較熱門的框架,社區也很活躍。這個框架應該是是功能最強大的,它的優點:它的哲理是:它的設計理念:相關限制:總結:在iOS部分是封裝了UIAutomation;Android4.2以上是用UiAutomator,Android2.3~4.1用的是Instrumentation,也就說Appium同時封裝了UiAutomator和Instrumentation。所以Appium擁有了以上幾大框架的所有優點:跨App,支持NativeApp、HybirdApp、WebApp,還支持N種語言來編寫你的測試腳本。如果你在Windows使用Appium,你沒法使用預編譯專用於OSX的.app文件,因為Appium依賴OSX專用的庫來支持iOS測試,所以在Windows平台你不能測試iOSApps。這意味著你只能通過在Mac上來運行iOS測試。Client/Server架構,運行的時候Server端會監聽Client端發過來的命令,翻譯這些命令發送給移動設備或模擬器,然後移動設備或模擬器做出響應的反應。正是因為這種架構,所以Client可以使用Appiumclientlibraries多種語言的測試腳本,而且Server端完全可以部署在伺服器上,甚至雲伺服器。Session,每個Client連接到Server以後都會有一個SessionID,而且Client發送命令到Server端都需要這個SessionID,因為這個seesionid代表了你所打開的瀏覽器或者是移動設備的模擬器。所以你甚至可以打開N個Session,同時測試不同的設備或模擬器。DesiredCapabilities,其實就是一個鍵值對,設置一些測試的相關信息來告訴Server端,我們需要測試iOS、還是Android,或者換是WebApp等信息。AppiumServer是Node.js寫的,所以可以直接用NPM來進行安裝。AppiumClients,MacOS和Win下提供GUI,不需要裝Node.js,方便測試人員操作。用Appium自動化測試不需要重新編譯App;支持很多語言來編寫測試腳本,Java、Javascript、PHP、Python、C#、Ruby等主流語言;不需要為了自動化測試來重造輪子,因為擴展了WebDriver。(WebDriver是測試WebApps的一種簡單、快速的自動化測試框架,所以有Web自動化測試經驗的測試人員可以直接上手);移動端自動化測試應該是開源的;開源;支持NativeApp、HybirdApp、WebApp;支持Android、iOS、FirefoxOS;Server也是跨平台的,你可以使用MacOSX、Windows或者Linux;

C. 如何設計Android App測試用例

一般安卓開發者在其日常工作中面臨的最大挑戰之一是:終端設備和[url=]操作系統[/url]版本的范圍太廣。OpenSignal進行的一項研究表明,2013年7月市場上有超過11,828的不同安卓終端設備,所有設備在類型/大小/屏幕解析度以及特定配置方面有所不同。考慮到前一年的調查僅記錄有3,997款不同設備,這實在是一個越來越大的挑戰障礙。

從一個移動APP開發角度出發,定義終端設備有四個基本特徵:
1.操作系統:由「API指標」( 1 ~18 )專業定義的安卓操作系統版本( 1.1~ 4.3 ),。
2.顯示器:屏幕主要是由屏幕解析度(以像素為單位),屏幕像素密度( 以DPI為單位),和/或屏幕尺寸(以英寸為單位)定義的。
3.CPU:該「應用程序二進制介面」 (ABI )定義CPU的指令集。這里的主要區別是ARM和基於Intel的CPU。
4.內存:一個設備包括內存儲器( RAM)和Dalvik 虛擬存儲器( VM堆)的預定義的堆內存。
這是前兩個特點,操作系統和顯示器,都需要特別注意,因為他們是直接由最終用戶明顯感受,且應該不斷嚴格地被測試覆蓋。至於安卓的版本, 2013年7月市場上有八個同時運行導致不可避免的碎片的不同版本。七月,近90%這些設備中的34.1 %正在運行Gingerbread版本( 2.3.3-2.3.7 ),32.3 %正在運行Jelly Bean( 4.1.x版),23.3 %正在運行Ice Cream Sandwich( 4.0.3 - 4.0.4 )。

考慮設備顯示器,一項TechCrunch從2013年4月進行的研究顯示,絕大多數(79.9%)有效設備正在使用尺寸為3和4.5英寸的「正常」屏幕。這些設備的屏幕密度在「MDPI」(~160 DPI),「hdpi」(~240 DPI)和「xhdpi」(~320 DPI)之間變化。也有例外, 一種只佔9.5%的設備屏幕密度低「hdpi」(~120 DPI)且屏幕小。

如果這種多樣性在質量保證過程中被忽略了,那麼絕對可以預見:bugs會潛入應用程序,然後是bug報告的風暴,最後Google Play Store中出現負面用戶評論。因此,目前的問題是:你怎麼使用合理水平的測試工作切實解決這一挑戰?定義測試用例及一個伴隨測試過程是一個應付這一挑戰的有效武器。

用例—「在哪測試」、「測試什麼」、「怎麼測試」、「何時測試」?
「在哪測試」
為了節省你測試工作上所花的昂貴時間,我們建議首先要減少之前所提到的32個安卓版本組合及代表市場上在用的領先設備屏的5-10個版本的顯示屏。選擇參考設備時,你應該確保覆蓋了足夠廣范圍的版本和屏幕類型。作為參考,您可以使用OpenSignal的調查或使用手機檢測的信息圖[3],來幫助選擇使用最廣的設備。
為了滿足好奇心,可以從安卓文件[5]將屏幕的尺寸和解析度映射到上面數據的密度(「ldpi」,「mdpi」等)及解析度(「小的」,「標準的」,等等)上。

有了2013手機檢測研究的幫助,很容易就找到了代表性的一系列設備。有一件有趣的瑣事:30%印度安卓用戶的設備解析度很低只有240×320像素,如上面列表中看到的,三星Galaxy Y S5360也在其中。另外,480×800解析度像素現在最常用(上表中三星Galaxy S II中可見)。

「測試什麼」
移動APP必須提供最佳用戶體驗,以及在不同尺寸和解析度(關鍵字「響應式設計」)的各種智能手機和平板電腦上被正確顯示(UI測試)。與此同時,apps必須是功能性的和兼容的(兼容性測試),有盡可能多的設備規格(內存,CPU,感測器等)。加上先前獲得的「直接」碎片化問題(關於安卓的版本和屏幕的特性), 「環境相關的」碎片化有著舉足輕重的作用。這種作用涉及到多種不同的情況或環境,其中用戶正在自己的環境中使用的終端設備。作為一個例子,如果網路連接不穩定,來電中斷,屏幕鎖定等情況出現,你應該慎重考慮壓力測試[4]和探索性測試以確保完美無錯。

有必要提前准備覆蓋app最常用功能的所有可能的測試場景。早期bug檢測和源代碼中的簡單修改,只能通過不斷的測試才能實現。

「怎麼測試」
將這種廣泛的多樣性考慮在內的一種務實方法是, 安卓模擬器 - 提供了一個可調節的工具,該工具幾乎可以模仿標准PC上安卓的終端用戶設備。簡而言之,安卓模擬器是QA流程中用各種設備配置(兼容性測試)進行連續回歸測試(用戶界面,單元和集成測試)的理想工具。探索性測試中,模擬器可以被配置到一個范圍廣泛的不同場景中。例如,模擬器可以用一種能模擬連接速度或質量中變化的方式來設定。然而,真實設備上的QA是不可缺少的。實踐中,用作參考的虛擬設備依然可以在一些小的(但對於某些應用程序來說非常重要)方面有所不同,比如安卓操作系統中沒有提供程序特定的調整或不支持耳機和藍牙。真實硬體上的性能在評價過程中發揮了自身的顯著作用,它還應該在考慮了觸摸硬體支持和設備物理形式等方面的所有可能終端設備上進行測試(可用性測試)。

「何時測試」
既然我們已經定義了在哪裡(參考設備)測試 ,測試什麼(測試場景),以及如何( 安卓模擬器和真實設備)測試,簡述一個過程並確定何時執行哪一個測試場景就至關重要了。因此,我們建議下面的兩級流程:
1 .用虛擬設備進行的回歸測試。
這包括虛擬參考設備上用來在早期識別出基本錯誤的連續自動化回歸測試。這里的理念是快速地、成本高效地識別bugs。
2 .用真實設備進行的驗收測試。
這涉及到:「策劃推廣」期間將之發布到Google Play Store前在真實設備上的密集測試(主要是手動測試),(例如,Google Play[ 5 ]中的 alpha和beta測試組) 。
在第一階段,測試自動化極大地有助於以經濟實惠的方式實現這一策略。在這一階段,只有能輕易被自動化(即可以每日執行)的測試用例才能包含在內。
在一個app的持續開發過程中,這種自動化測試為開發人員和測試人員提供了一個安全網。日常測試運行確保了核心功能正常工作,app的整體穩定性和質量由測試數據透明地反映出來,認證回歸可以輕易地與最近的變化關聯。這種測試可以很輕易地被設計並使用SaaS解決方案(如雲中的TestObject的UI移動app測試)從測試人員電腦上被記錄下來。
當且僅當這個階段已被成功執行了,這個過程才會在第二階段繼續勞動密集測試。這里的想法是:如果核心功能通過自動測試就只投入測試資源,使測試人員能夠專注於先進場景。這個階段可能包括測試用例,例如性能測試,可用性測試,或兼容性測試。這兩種方法相結合產生了一個強大的移動apps質量保證策略[ 7 ] 。

結論 - 做對測試
用正確的方式使用,測試可以在對抗零散的安卓的斗爭中成為一個有力的工具。一個有效的測試策略的關鍵之處在於定義手頭app的定製測試用例,並定義一個簡化測試的工作流程或過程。測試一個移動app是一個重大的挑戰,但它可以用一個結構化的方法和正確的工具集合以及專業知識被有效解決掉。

D. 如何一步步實現AndroidCI

一步步實現Android CI

Android上的CI構建鏈與其它平台一致,依然包含Compilation, Testing, Inspection,
Deploying階段,每一個階段的Feedback的都保持對整個團隊透明。

2、添加Function Test


Android為大家提供了一套集成測試框架Android integration testing
framework。但此框架未集成Cucumber,這導致每增加一個Function Test都需要較大的開發和維護工作。這樣高成本的實現Function
Test將大大延緩開發進度,最終因為項目進度的原因導致Function Test被丟棄。產生這樣的後果那必然是不願意看到的。


目前Android平台下已經出現多種Functiong Testing測試工具,如Native Driver, Robotium,
Calabash等。在嘗試對比後,最終選擇了Calabash Android作為解決方案。Calabash
Android是Cucumber在Android平台的實現,使用Ruby書寫Function Test,並提供了一組操作Anadroid App元素的API。

3、添加UI Test


Android在新近退出了UI測試工具UIAutomator。此工具僅支持Android4.1及以上平台,鑒於目前市場上2.3和4.0版本仍佔主導的情況來看,目前還無法滿足大家的需要。另外應用該工具實現UI測試的開發成本還較高,筆者暫不推薦使用此工具,但應該關注其發展。


另外基於錄制回放機制的測試方法同樣可以進行UI測試。但錄制回放的方法在面對功能快速迭代時,維護工作會急劇增加,而這個維護成本可以說是很難承受的,所以在此也不會將這種測試方法集成至CI中。


目前來看Android中UI測試還無令人滿意的方法。若對UI成功比較看重,可以投入精力應用UIAutomator進行UI測試。

Best Practice:


*
將測試按照單元測試,組件測試,功能測試和系統測試進行劃分。單元測試應該在每次提交時觸發執行,其它的測試根據運行時間長短和重要程度可以每次提交觸發執行或者定時周期執行。


* 將運行較快的測試優先執行。


* 讓功能測試能夠重復執行。否則維護成本太高,會被舍棄。若是後台數據導致不可重復,可以將數據抽象成為數據集,在每次運行前進行重置。


* 書寫測試時每一個assert只做一種判斷,這樣可以明確每次測試的目的,並且可以快速定位測試失敗願意。

步驟 3:持續檢查持續檢查是對於代碼本身檢測和反饋。檢測主要通過對代碼靜態分析驗證代碼風格,編程規范,代碼復用,代碼語言中的Best Practice等多個維度的代碼質量。

Sonar作為一個開源的代碼質量檢測工具,涵蓋了7項代碼質量檢測方式。這充分滿足Android平台下對於代碼質量的檢測分析。Sonar分為兩部分一部分是代碼分析工具,另一部分是數據分析展示的Server。

Best Practice:


* 將測試覆蓋率,代碼分析結果透明化


* 持續降低代碼復雜度


* 持續的促進設計的演進


* 持續的維護代碼結構


* 持續減少代碼重復

步驟 4:持續部署


由於Android App採用用戶手動從Appstore自行下載安裝的方式發布,使得Android
App無法直接部署至用戶手機中。另外Appstore需要對於上線的App進行審核,不能持續進行Release。因而Android中持續部署將以持續發布可安裝包為目標。


在以上目的下,只需根據自身項目資源找到合適的安裝包管理工具即可。如本文採用Dropbox來管理所有安裝包。


Dropbox作為一個雲存儲平台,在Android終端設備上可以輕松下載存放在其中的文件,同時上傳安裝包也可以交由Dropbox自己完成。

步驟 5:持續反饋


反饋是所有改進的開始,必須要讓所有人獲取到他們所關心的反饋信息,才能實施改進。持續反饋的目的就是讓所有人都掌握項目健康狀況。項目所有人事實都是有意願知道項目當前的健康狀況的,那CI就應該將項目的情況做到透明,並將不同的反饋通知到各相關的成員。


CI不同階段產生了不同維度的反饋,如單元測試報告,測試覆蓋率等。本實踐中將這些反饋都透明的展示在項目首頁中。之所以沒有將這些反饋再以郵件的方式通知所有人,是因為團隊成員已經養成了查看CI的習慣。


如果說只給所有人發一封郵件說明項目狀況,那必然是告訴所有人「CI所有步驟是否都返回正確?」。這樣一個反饋,包含了編譯正確,所有測試通過,安裝包已經准備完畢等重要信息。有必要讓所有人都知道這個信息,特別是在CI執行失敗的時候。Jenkins自身已經提供一個簡單有效的透明化方法,以項目為藍色表示通過,紅色表示有步驟失敗。


反饋的通知方式有很多種,不一定要採用郵件通知的方式。可以尋找更加有趣的方式,如果播放音樂和設置警報燈。在每一次Build成功或失敗後都播放一段有趣的音樂,打開不同顏色的警報燈,這兩種方法都是是一種簡單有效的方式,可以讓項目所有人都獲取到最為關鍵的信息。

E. 做Android手機軟體測試需要掌握哪些工具

請注意測試本身不是靠工具的而是靠設計,這是我的理念,所以我一向覺得,很多人認為做測試做的好就是靠掌握一門好的工具,這個觀點是不正確的,所以我可以負責任的告訴你,做Android手機需要掌握的不是工具、而是理念、思維、以及框架,總的來說是本質,而工具只是輔助,那麼現在我來介紹一些我了解的工具(僅僅是了解,很多沒用過)
開源 Android 軟體測試工具包括:Android Test Kit, AndroidJUnit4, Appium, calabash-android, Monkey, MonkeyTalk, NativeDriver, Robolectric, RoboSpock, Robotium, UIAutomator, Selendroid。
Android Test Kit
Android Test Kit 是一組 Google 開源測試工具,用於 Android 平台,包含 Espresso API 可用於編寫簡潔可靠的 Android UI 測試。
AndroidJUnit4
AndroidJUnit4 是一個讓 JUnit 4 可以直接運行在 Android 設備上的開源命令行工具。
Appium
Appium 是一個開源、跨平台的自動化測試工具,用於測試原生和輕量移動應用,支持 iOS, Android 和 FirefoxOS 平台。Appium 驅動蘋果的 UIAutomation 庫和 Android 的 UiAutomator 框架,使用 Selenium 的 WebDriver JSON 協議。Appinm 的 iOS 支持是基於 Dan Cuellar's 的 iOS Auto. Appium 同時綁定了 Selendroid 用於老的 Android 平台測試。
Calabash-android
calabash-android 是一個基於 Cucumber 的 Android 的功能自動化測試框架。Calabash 允許你寫和執行,是開源的自動化移動應用測試工具,支持 Android 和 iOS 原生應用。Calabash 的庫允許原生和混合應用的交互測試,交互包括大量的終端用戶活動。Calabash 可以媲美 Selenium WebDriver。但是, 需要注意的是 web 應用和桌面環境的交互跟觸摸屏應用的交互是不同的。Calabash 專為觸摸屏設備的原生應用提供 APIs。
Monkey
Monkey 是 Google 開發的 UI/應用測試工具,也是命令行工具,主要針對壓力測試。你可以在任意的模擬器示例或者設備上運行。Monkey 發送一個用戶事件的 pseudo-random 流給系統,作為你開發應用的壓力測試。
MonkeyTalk
MonkeyTalk 是世界上最強大的移動應用測試工具。MonkeyTalk 自動為 iOS 和 Android 應用進行真實的,功能性交互測試。MonkeyTalk 提供簡單的 "smoke tests",復雜數據驅動的測試套件。MonkeyTalk 支持原生,移動和混合應用,真實設備或者模擬器。MonkeyTalk 使得場景捕獲非常容易,可以記錄高級別,可讀的測試腳本。同樣的命令可以用在 iOS 和 Android 應用上。你可以記錄一個平台的一個測試,並且可以在另外一個平台回放。MonkeyTalk 支持移動觸摸和基於手勢交互為主的移動體驗。點擊,拖拽,移動,甚至是手指繪制也可以被記錄和回放。
NativeDriver
NativeDriver 是 WebDriver API 的實現,是原生應用 UI 驅動,而不是 web 應用。
Robolectric
Robolectric 是一款Android單元測試框架,使用 Android SDK jar,所以你可以使用測試驅動開發 Android 應用。測試只需幾秒就可以在工作站的 JVM 運行。Robolectric 處理視圖縮放,資源載入和大量 Android 設備原生的 C 代碼實現。Robolectric 允許你做大部分真實設備上可以做的事情,可以在工作站中運行,也可以在常規的 JVM 持續集成環境運行,不需要通過模擬器。
RoboSpock
RoboSpock 是一個開源的 Android 測試框架。提供簡單的編寫 BDD 行為驅動開發規范的方法,使用Groovy 語音,支持 Google Guice 庫。RoboSpock 合並了 Robolectric 和 Spock 的功能。
Robotium
Robotium 是一款國外的Android自動化測試框架,主要針對Android平台的應用進行黑盒自動化測試,它提供了模擬各種手勢操作(點擊、長 按、滑動等)、查找和斷言機制的API,能夠對各種控制項進行操作。Robotium結合Android官方提供的測試框架達到對應用程序進行自動化的測 試。另外,Robotium 4.0版本已經支持對WebView的操作。Robotium 對Activity,Dialog,Toast,Menu 都是支持的。
UIAutomator
uiautomator 測試框架提高用戶界面(UI)的測試效率,通過自動創建功能 UI 測試示例,可以在一個或者多個設備上運行你的應用。
Selendroid
Selendroid 是一個 Android 原生應用的 UI 自動化測試框架。測試使用 Selenium 2 客戶端 API 編寫。Selendroid 可以在模擬器和實際設備上使用,也可以集成網格節點作為縮放和並行測試。

F. 用android uiautomator做自動化測試,怎麼連接真機進行

Google在sdk4.0以後提供了一個自動化解決方案uiautomator:

優點:可以跨應用了;這可是親生的;

缺點:必須sdk4.0以上版本;要想實現的好,最好有開發配合;java項目編譯為jar後需要push到手機才能運行,也就是說必須列印日誌暴力調試。

Appium基於Android InstrumentationFramework和UIAutomator,也就是說這個工具是可以跨應用的。說遠了,好吧,為了幫大家更容易理解appium的使用,我這里就講一下uiautomator的使用方法。

你應該有android-sdk吧,升級到4.0以上,進入目錄android-sdk ools,你會看到兩個文件:

traceview.bat 和 uiautomatorviewer.bat,這倆文件讓你想起了monkeyrunner了吧,是的,traceview.bat就對應於hierarchyviewer.bat,用來查看程序的ui界面的,通常也是使用管理員許可權啟動的。

好了,現在用eclipse創建一個java project,是的,你沒看錯,是java project不是android project,添加引用:

在project.properties中內容為:

# Project target.

target=android-16

這里的android-16需要和之前的android.jar和uiautomator.jar位置相一致。

然後呢?寫代碼吧,建立一個類,得,發個給大家參考:

packagecom.uia.example.my;

importorg.apache.http.util.EncodingUtils;

importandroid.graphics.Bitmap;

importandroid.graphics.BitmapFactory;

importandroid.graphics.Rect;

importandroid.os.Environment;

importcom.android.uiautomator.core.UiObject;

importcom.android.uiautomator.core.UiObjectNotFoundException;

importcom.android.uiautomator.core.UiScrollable;

importcom.android.uiautomator.core.UiSelector;

importcom.android.uiautomator.testrunner.UiAutomatorTestCase;

importjava.io.File;

importjava.io.FileOutputStream;

importjava.io.IOException;

{

publicStringsLog;

publicFilefout=null;

=null;

publicvoidwrite2file(String filename,String sData)

{

String sLog="";

//初始化日誌文件

if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){

sLog = Environment.getExternalStorageDirectory().getAbsolutePath();

try{

fout=newFile(sLog,filename);

outStream=newFileOutputStream(fout,true);//此處的true是append

sData=sData +" ";

outStream.write(sData.getBytes());

outStream.flush();

outStream.close();

fout=null;

}

catch(Exception e){

e.printStackTrace();

}

}else{

System.out.println("該手機沒有SD卡");

}

}

publicvoidtestDemo() {

//1.啟動app

getUiDevice().pressHome();

UiObject allAppsButton =newUiObject(newUiSelector().description("Apps"));

allAppsButton.clickAndWaitForNewWindow();

UiObject appsTab =newUiObject(newUiSelector().text("Apps"));

appsTab.click();

UiScrollable appViews =newUiScrollable(newUiSelector().scrollable(true));

UiObject settingsApp = appViews.getChildByText(newUiSelector().className(android.widget.TextView.class.getName()),"Efilm");

settingsApp.clickAndWaitForNewWindow();

//2.進入主界面

System.out.println("into main view");

System.out.println(getUiDevice().waitForWindowUpdate("com.eshore.efilm", 60000));

System.out.println("intoed main view");

UiObject tv1 =newUiObject(newUiSelector().text("影院"));

tv1.click();

//3.點擊影院

UiObject oyy=newUiObject(newUiSelector().description("cinema_row"));

System.out.println("wait yingyuan come out");

oyy.waitForExists(60000);

System.out.println("yingyuan come out");

oyy.clickAndWaitForNewWindow();

System.out.println("click yingyuan");

//4.場次

UiObject occ=newUiObject(newUiSelector().description("LinearLayout10"));

System.out.println("wait changci come out");

oyy.waitForExists(60000);

System.out.println("changci come out");

occ.clickAndWaitForNewWindow();

System.out.println("click changci");

//5.座位

UiObject oseat=newUiObject(newUiSelector().description("cinema_shows_list_item").index(0).childSelector(newUiSelector().description("LinearLayout10")));

System.out.println("wait seat come out");

oseat.waitForExists(5000);

inth=getUiDevice().getDisplayHeight();

intw=getUiDevice().getDisplayWidth();

System.out.println("(h/2,w/2)="+h/2+","+w/2);

getUiDevice().click(h/2,w/2);

//System.out.println("seat count:"+String.valueOf(oseat.getChildCount()));

//System.out.println("seat getText:"+ oseat.getText());

//截座點陣圖

Process process;

try{

process = Runtime.getRuntime().exec("screencap /mnt/sdcard/EfilmFailSnapShot01.png");

try{

process.waitFor();

}catch(InterruptedException e) {//TODOAuto-generated catch block

e.printStackTrace();

}

}catch(IOException e) {

//TODOAuto-generated catch block

e.printStackTrace();

}

//takeScreenShots("EfilmSeatSnapShot");

}

}

這個例子是隨便寫的,可能不夠嚴謹。大體就這么個情況吧。下一步就是編譯執行了,先插上手機usb介面,然後打開cmd,執行:

找到SDKID,也就是android create中的-t參數:

cd C: PROGRAMandroid-sdk ools

android list

找到t參數的值以後:

cd C: PROGRAMandroid-sdk ools

android create uitest-project -n TAppWorkAssistV1 -t 25 -p C:android自動化Tv2.0TestSetting

cd C:android自動化Tv2.0TestSetting

ant build

cd C:android自動化Tv2.0TestSettingin

adb push TAppWorkAssistV1.jar /data/local/tmp/

adb shell uiautomator runtest TAppWorkAssistV1.jar -c com.uia.example.my. TAppWorkAssistV1

看了看,好像沒有什麼特別值得解釋的

-n TAppWorkAssistV1:類名

-p: 項目所在目錄

Ant build 把這個類編譯成一個jar包:TAppWorkAssistV1.jar

然後把jar包push到手機上,調用執行這個類就可以了

大致是這么個步驟,不過有一個非常重要的細節,就是如果你需要更省心,就最好把界面元素,無論動態的還是布局文件中的,都加上content-description屬性,並保證唯一性,根據:

UiSelector:description(String desc)

Set the search criteria to match thecontent-description property for a widget.

那就可以統一隻使用這一個引用界面元素的方法就行了,就不用去想方設法利用其它的屬性來引用了。

G. android 鎬庝箞鍗曠嫭嫻嬭瘯ctsuirenderingtestcases

android涓嬩篃鏄鍙浠ョ殑錛屾瘡涓宸ョ▼鐨勫惎鍔ㄩ」閮芥灊鎼滄槸涓涓猘ctivity錛屼綘鍙瑕佹妸activity鐨勬帴鍙g暀鍑烘潵(鍏跺疄涓嶉渶瑕佺暀鍑烘潵錛屽洜涓哄惎鍔ㄤ竴涓猘ctivity鍙闇瑕乻tartactivity()鏂規硶灝卞彲浠ヤ簡)錛氫緥濡備綘鎯寵佷竴涓紼嬪簭閲岄潰鏃㈡湁璁$畻鍣錛屽張鏈夐槄璇伙紝浣犲彲浠ュ緩絝嬩竴涓猘ctivity錛岃繖涓猘ctivity閲岄潰鏈変袱涓鎸夐挳錛屼竴涓鏄寮鍚璁$畻鍣錛屼竴涓鏄寮鍚闃呰伙紝鍒嗗埆鎸囧悜鐩稿簲鐨勭▼搴忥紝媧炵粷榪欐牱錛屼綘鐨勪竴涓宸ョ▼灝卞惈鏈夊氫釜鍗曠嫭鐨勫簲鐢ㄧ▼搴忎簡鍟娿 妤間富錛屼唬鐮佹槸姝葷殑錛岀▼搴忔槸緇欎漢鐢ㄧ殑錛屾垜浠闇瑕佸仛鐨勬槸鏍規嵁姝葷殑浠g爜鍋氬嚭媧葷撼鎼濮跨殑涓滆タ鏉ワ紝鍔犳補錛1

H. android如何產生隨機數

android產生隨機數的方法:此方法通過把當前時刻長整型數傳給Random對象,讓它產生的值隨著時間而變化。

[java] view plain
Strings = "";
Random ran =new Random(System.currentTimeMillis());
for (inti = 0; i < 10; i++) {
s =s + "\n" + ran.nextInt(100);
}
Toast.makeText(UiTestActivity.this,"Random: \n" + s, Toast.LENGTH_LONG).show();

I. 如何使用python做android的自動化測試

開始第一個簡單的Android UI自動化測試
1.使用adb命令連接真機或模擬器
2.打開uiautomatorviewer工具
3.使用uiautomatorviewer工具獲取應用的元素進行定位
4.簡單介紹unittest框架的使用方法
5.使用Python編寫貓寧考勤應用注冊模塊的自動化測試
1.使用adb命令連接真機或模擬器:
手機USB連接電腦,進入開發者模式;
cmd命令:adb devices ,查看手機是否連接
這里寫圖片描述
顯示錯誤
這是因為adb的埠被佔用,我們需要查看是什麼應用佔用了這個埠(5037為adb默認埠)
cmd命令 : netstat -aon|findstr 「5037」
這里寫圖片描述
可以看到佔用5037埠對應的程序的PID號為8388;
cmd命令 : tasklist|findstr 「8388」
這里寫圖片描述
可以看出8388對應的程序為kadb.exe,說明該程序正在使用5037埠;
這時我們需要在任務管理器中結束kadb.exe進程,按快捷鍵「Ctrl+Shift+Esc」調出Windows任務管理器,找到「kadb.exe」,單擊下方的結束進程即可!
這里寫圖片描述
我們再次運行cmd命令:adb devices
這里寫圖片描述
這一步成功後我們才能運行sdk自帶的uiautomatorviewer;
我們需要用uiautomatorviewer工具來獲取元素,用於定位。
cmd命令:uiautomatorviewer,打開uiautomatorviewer界面
這里寫圖片描述
或者找到sdk目錄:sdk\tools中找到uiautomatorviewer.bat文件雙擊運行
這里寫圖片描述
2.打開uiautomatorviewer工具
這里寫圖片描述
我們可以根據text,resource-id,class等元素進行定位
3.使用uiautomatorviewer工具獲取應用的元素進行定位
這里我使用python自帶的IDLE進行編寫測試腳本,打開python文件找到IDLE(python GUI)雙擊打開,如圖:
這里寫圖片描述
4.簡單介紹unittest框架的使用方法
# -*- coding:utf-8 -*-
from uiautomator import device as d
import unittest
class Mytest(unittest.TestCase):
#初始化工作
def setUp(self):
print "--------------初始化工作"
#退出清理工作
def tearDown(self):
print "--------------退出清理工作"
#測試點擊貓寧考勤case
def test_login(self):
d(text="貓寧考勤").click()
print "--------------測試1"
#測試2
def test_z(self):
print "--------------測試2" #這里你可以寫你的第二個測試用例,
#測試3
def test_w(self):
print "--------------測試3" #這里你可以寫你的第三個測試用例。。。。。。。。。。。。。
if __name__ == '__main__':
unittest.main()
結果如下:
Testing started at 21:14 …
————–初始化工作
————–測試1
————–退出清理工作
————–初始化工作
————–測試3
————–退出清理工作
————–初始化工作
————–測試2
————–退出清理工作
Process finished with exit code 0
從結果中我們可以看出unittest框架的運行方式為:
setUp 測試1 tearDown
setUp 測試2 tearDown
setUp 測試3 tearDown
5.使用Python編寫貓寧考勤應用注冊模塊的自動化測試
# -*- coding:utf-8 -*-
from uiautomator import device as d
import time
import unittest
class MyTestSuite(unittest.TestCase):
# 初始化工作
def setUp(self):
print "--------------初始化工作"
# 退出清理工作
def tearDown(self):
print "--------------退出清理工作"
#***************************方法**************************************
# 判斷控制項是否存在 & text
def check_controls_exists(self, controls_text):
if d(text=controls_text).exists:
return 1
else:
return 0
# 判斷按鈕是否置灰 & text & clickable
def check_controls_click_text(self, controls_text):
if d(text=controls_text).info.get("clickable") is True:
return 1
else:
return 0
#assertIn(a, b) a in b
def check_ainb(self,resourceid,b):
if d(resourceId=resourceid).info.get("text") in b:
return 1
else:
return 0
#***********************************************************
# 注冊模塊
def test_Aregister(self):
try:
time.sleep(2)
#貓寧考勤開啟全新時代
self.assertEqual(self.check_controls_click_text("注冊"),1,u"貓寧考勤開啟全新時代")
# 貓寧考勤開啟全新時代--》點擊注冊按鈕進入注冊貓寧界面
d(text="注冊").click()
time.sleep(3)
#注冊貓寧界面
self.assertEqual(self.check_text("com.isentech.attendancet:id/regis_phone","請輸入手機號碼"),
1,u"注冊頁面-》請輸入手機號碼")
self.assertEqual(self.check_text("com.isentech.attendancet:id/regis_verifycode","請輸入驗證碼"),
1,u"注冊頁面-》請輸入驗證碼")
self.assertEqual(self.check_controls_click_text("獲取驗證碼"), 0,u"注冊頁面-》獲取驗證碼")
self.assertEqual(self.check_controls_click_text("《中科愛訊服務協議》"), 1,u"注冊頁面-》《中科愛訊服務協議》")
self.assertEqual(self.check_controls_click_text("注冊"), 0,u"注冊頁面-》注冊")
time.sleep(2)
#《中科愛訊服務協議》
d(text="《中科愛訊服務協議》").click()
time.sleep(2)
self.assertEqual(self.check_ainb("com.isentech.attendancet:id/title","服務協議"), 1,u"注冊頁面-》服務協議")
time.sleep(1)
d(resourceId="com.isentech.attendancet:id/title_back").click()
time.sleep(1)
#手機號不輸入是否能注冊
d(text="注冊").click()
time.sleep(3)
# 手機號只輸入1個數字是否能注冊&只輸入1個數字是否能獲取驗證碼
d(resourceId="com.isentech.attendancet:id/regis_phone").set_text("1")
self.assertEqual(self.check_controls_click_text("獲取驗證碼"), 0)
time.sleep(1)
d(text="注冊").click()
time.sleep(1)
d(resourceId="com.isentech.attendancet:id/regis_phone").clear_text()
time.sleep(1)
#只輸入5個數字是否能獲取驗證碼
d(resourceId="com.isentech.attendancet:id/regis_phone").set_text("11111")
self.assertEqual(self.check_controls_click_text("獲取驗證碼"), 0)
time.sleep(1)
d(resourceId="com.isentech.attendancet:id/regis_phone").clear_text()
time.sleep(1)
#只輸入手機號是否能注冊
d(resourceId="com.isentech.attendancet:id/regis_phone").set_text(phone_number)
self.assertEqual(self.check_controls_click_text("注冊"), 0)
time.sleep(1)
d(text="注冊").click()
time.sleep(1)
#輸入正確的驗證碼&獲取驗證碼是否高亮
d(resourceId="com.isentech.attendancet:id/regis_verifycode").set_text("5648")
time.sleep(1)
self.assertEqual(self.check_controls_click_text("獲取驗證碼"), 1)
time.sleep(2)
#密碼只輸入1個數字是否能注冊&注冊按鈕是否高亮
d(resourceId="com.isentech.attendancet:id/regis_pass").set_text("1")
d(resourceId="com.isentech.attendancet:id/regis_passAgain").set_text("1")
time.sleep(1)
self.assertEqual(self.check_controls_click_text("注冊"), 0,u"密碼只輸入1個數字是否能注冊")
time.sleep(1)
d(resourceId="com.isentech.attendancet:id/regis_pass").clear_text()
d(resourceId="com.isentech.attendancet:id/regis_passAgain").clear_text()
time.sleep(1)
#輸入不相同的密碼是否能注冊
d(resourceId="com.isentech.attendancet:id/regis_pass").set_text("123456")
d(resourceId="com.isentech.attendancet:id/regis_passAgain").set_text("12345")
time.sleep(1)
self.assertEqual(self.check_controls_click_text("注冊"), 0,u"輸入不相同的密碼是否能注冊")
time.sleep(1)
d(resourceId="com.isentech.attendancet:id/regis_pass").clear_text()
d(resourceId="com.isentech.attendancet:id/regis_passAgain").clear_text()
time.sleep(1)
#輸入正確的密碼是否能注冊&我已同意是否打鉤
d(resourceId="com.isentech.attendancet:id/regis_pass").set_text("123456")
d(resourceId="com.isentech.attendancet:id/regis_passAgain").set_text("123456")
time.sleep(1)
self.assertEqual(self.check_controls_click_resourceId("com.isentech.attendancet:id/regis_agree"), 1)
self.assertEqual(self.check_controls_click_text("注冊"), 1)
time.sleep(2)
d(text="注冊").click()
time.sleep(8)
except Exception, e:
print u"Error: 注冊模塊有問題\n", e
def test_app():
test_unit = unittest.TestSuite()
test_unit.addTest(MyTestSuite("test_Aregister"))
if __name__ == "__main__":
# 測試app
unittest.main()

閱讀全文

與androiduitest相關的資料

熱點內容
黑貓電影官網 瀏覽:557
韓國推理片免費電影 瀏覽:824
基於nfc的android 瀏覽:34
電影《血戰高台》免費觀看 瀏覽:786
數據結構筆記pdf 瀏覽:682
龍之谷為什麼老是與伺服器斷開 瀏覽:580
程序員單身狀況 瀏覽:888
美國要怎麼下載國內抖音app 瀏覽:593
交叉編譯其硬體構成有哪些 瀏覽:161
博奧雲計價需要加密狗嗎 瀏覽:194
伺服器fpt什麼意思 瀏覽:660
易語言加密EXE解密後運行 瀏覽:433
盲文app哪個好 瀏覽:376
孤島驚魂6是d加密嗎 瀏覽:132
編譯技術的未來前景 瀏覽:922
汽車分散演算法 瀏覽:920
多視覺幾何pdf 瀏覽:884
網速伺服器是什麼 瀏覽:156
科學出版社編譯手冊 瀏覽:6
哪裡可以看槍火 瀏覽:213