Appium+Python-項目實踐一

一、前言                           

  前面講了環境搭建和常用的元素定位,後續會持續以項目實踐的方式去慢慢學習以及整理各方面的知識點,具體不會詳細闡述,但會貼上完整代碼,想要了解更多的可以直接網上查找資料哈,接下來用企業微信的應用(訂單平台)做實踐,環境配置:win10+Python 3.7.4+appium 1.16.0+unittest框架+真機andorid 8.0

二、關於Appium實現中文輸入  

  1、要實現中文輸入的話,在初始化里加入下面的代碼就行了

# 實現中文輸入,使用unicodeKeyboard的編碼方式來發送字符串
'unicodeKeyboard': True,  
# 將鍵盤給隱藏起來
'resetKeyboard': True  

  2、如果Appium設置中文輸入報錯: Attempt to re-install io.appium.android.ime without first

    原因:漏卸載應用

    解決方法:查出所有的第三方安裝包:一共3個,然後用adb卸載掉(adb shell pm list package -3或adb shell pm list package -3 | findstr appium),如圖1

(圖1:卸載應用)

  3、Appium 在 Android7.0 以上版本找不到元素的問題,解決方法:安裝Uiautomator2(使用npm安裝: npm install appium-uiautomator2-driver)

三、關於隱式等待和顯示等待  

  1、WebDriverWait():顯示等待,是針對於某個特定的元素設置的等待時間,在設置時間內,默認每隔一段時間檢測一次當前頁面某個元素是否存在,如果在規定的時間內找到了元素,則直接執行,即找到元素就執行相關操作,如果超過設置時間檢測不到則拋出異常。默認檢測頻率為0.5s,默認拋出異常為:NoSuchElementException

  WebDriverWait()一般由unitl()或until_not()方法配合使用,until()調用該方法提供的驅動作為一個參數,直到返回值為True,unitl_not()調用該方法提供的驅動作為一個參數,直到返回值為False 。

  用法示例:

 1 from selenium.webdriver.support.wait import WebDriverWait
 2 from selenium.webdriver.support import expected_conditions as EC
 3 from selenium.webdriver.common.by import By
 4 
 5 loc = ("xpath", "//android.view.View[contains(@text,'確定')]")
 6    try:
 7        # 顯示等待用法一:EC.presence_of_element_located()直到元素出現
 8        e = WebDriverWait(self.driver, 3, 0.5).until(EC.presence_of_element_located(loc))
 9        # 顯示等待用法二:用By
10        #WebDriverWait(self.driver, 3, 0.5).until(EC.presence_of_element_located(By.XPATH, "//android.view.View[contains(@text,'確定')]"))
11        # 顯示等待用法三:用lambda函數
12        #e = WebDriverWait(self.driver, 3 ,0.5).until(lambda x: self.driver.find_element_by_xpath("//android.view.View[contains(@text,'確定')]"))
13        e.click()
14        print("成功選擇非免付啦")
15     except:
16        pass

  2、implicitly_wait():隱式等待,是設置的全局等待。設置等待時間,是對頁面中的所有元素設置加載時間,如果超出了設置時間的則拋出異常。隱式等待可以理解成在規定的時間範圍內,瀏覽器在不停的刷新頁面,直到找到相關元素或者時間結束。

  3、隱式等待和顯示等待都存在時,超時時間取二者中較大的。

四、關於unittest框架     

  1、簡要說明

  unittest是Python中自帶的單元測試框架,它裏面封裝好了一些校驗返回的結果方法和一些用例執行前的初始化操作。unittest單元測試框架不僅可以適用於單元測試,還可以適用web自動化測試用例的開發與執行,該測試框架可組織執行測試用例,並且提供了豐富的斷言方法,判斷測試用例是否通過,最終生成測試結果。

最基礎的四個概念:TestCase,TestSuite,TestRunner,TestFixture

  2、運行流程

  先編寫好TestCase,然後由TestLoader加載TestCase到TestSuite,其次由TextTestRunner來運行TestSuite,運行的結果保存在TextTestResult中,我們通過命令行或者unittest.main()執行時,main會調用TextTestRunner中的run來執行,或者我們可以直接通過TextTestRunner來執行用例

  3、unittest模塊的各個屬性說明

  1)unittest.TestCase:TestCase類,所有測試用例類繼承的基本類。class BaiduTest(unittest.TestCase):

  2)unittest.main():使用它可以方便的將一個單元測試模塊變為可直接運行的測試腳本,main()方法使用TestLoader類來搜索所有包含在該模塊中以「test」命名開頭的測試方法,並自動執行他們。執行方法的默認順序是:根據ASCII碼的順序加載測試用例,數字與字母的順序為:0-9,A-Z,a-z。所以以A開頭的測試用例方法會優先執行,以a開頭會後執行。

  3)unittest.TestSuite():unittest框架的TestSuite()類是用來創建測試套件的。

  4)unittest.TextTextRunner():unittest框架的TextTextRunner()類,通過該類下面的run()方法來運行suite所組裝的測試用例,入參為suite測試套件。

  5)unittest.defaultTestLoader(): defaultTestLoader()類,通過該類下面的discover()方法可自動更具測試目錄start_dir匹配查找測試用例文件(test*.py),並將查找到的測試用例組裝到測試套件,因此可以直接通過run()方法執行discover。

  6)unittest.skip():裝飾器,當運行用例時,有些用例可能不想執行等,可用裝飾器暫時屏蔽該條測試用例。一種常見的用法就是比如說想調試某一個測試用例,想先屏蔽其他用例就可以用裝飾器屏蔽。

    @unittest.skip(reason): skip(reason)裝飾器:無條件跳過裝飾的測試,並說明跳過測試的原因。

    @unittest.skipIf(reason):skipIf(condition,reason)裝飾器:條件為真時,跳過裝飾的測試,並說明跳過測試的原因。

    @unittest.skipUnless(reason):skipUnless(condition,reason)裝飾器:條件為假時,跳過裝飾的測試,並說明跳過測試的原因。

    @unittest.expectedFailure():expectedFailure()測試標記為失敗。

  7)setUp():setUp()方法用於每個測試用例執行前的初始化工作。如測試用例中需要訪問數據庫,可以在setUp中建立數據庫連接並進行初始化。如測試用例需要登錄web,可以先實例化瀏覽器。

  8)tearDown():tearDown()方法用於每個測試用例執行之後的善後工作。如關閉數據庫連接、關閉瀏覽器。

  9)setUpClass():setUpClass()方法用於所有測試用例前的設置工作。

  10)tearDownClass():tearDownClass()方法用於所有測試用例執行後的清理工作。

  11)addTest():addTest()方法是將測試用例添加到測試套件中。

  12)run():run()方法是運行測試套件的測試用例,入參為suite測試套件。

  13)assert*():在執行測試用例的過程中,最終用例是否執行通過,是通過判斷測試得到的實際結果和預期結果是否相等決定的。

  4、添加用例運行方式

  1)用法一:用addTests方法單個添加用例

import unittest

if __name__ == '__main__':
    suite = unittest.TestSuite()
    # 單個添加測試用例
    suite.addTest(TestMathFunc("test_multi"))
    suite.addTest(TestMathFunc("test_divide"))
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

  2)用法二:將用例添加到一個用例集中,再通過suite統一運行

import unittest

if __name__ == '__main__':
    suite = unittest.TestSuite()
    # 將測試用例添加到一個用例集中
    tests = [TestMathFunc("test_add"), TestMathFunc("test_minus")]
    suite.addTests(tests)  # 將測試用例列表添加到測試組中
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(suite)

  3)用法三:這是最實用的方法,引用discover()方法,匹配指定文件夾下以test開頭的測試用例

import unittest

if __name__ == '__main__':
    path = './testcase'
    all_cases = unittest.defaultTestLoader.discover(path, 'test*.py')
    # 找到某個目錄下所有的以test開頭的Python文件裏面的測試用例
    runner = unittest.TextTestRunner(verbosity=2)
    runner.run(all_cases)

五、BeautifulReport測試報告  

  1、如何使用?

  1)pip安裝:pip install BeautifulReport

  2)使用示例:

import unittest
from BeautifulReport import BeautifulReport

if __name__ == '__main__':
    #"."表示當前目錄,"*tests.py"匹配當前目錄下所有tests.py結尾的用例
    test_suite = unittest.defaultTestLoader.discover('./tests', pattern='test*.py') 
   result = BeautifulReport(test_suite) 
   result.report(filename='測試報告', description='測試deafult報告', report_dir='report', theme='theme_default')

  2、簡要說明:

  1)BeautifulReport.report

    report (
        filename -> 測試報告名稱, 如果不指定默認文件名為report.html
        description -> 測試報告用例名稱展示
        report_dir=’.’ -> 報告文件寫入路徑
        theme=’theme_default’ -> 報告主題樣式 theme_default theme_cyan theme_candy theme_memories
        )

  2)BeautifulReport.add_test_img:如果使用報告過程中需要把測試報告的截圖放在報告中, 可以使用add_test_img方法

  3)add_test_img ( *pargs ):可以在測試用例上掛載一個裝飾器, 實例內容如下:

    默認存放的圖片路徑是img, 需要在當前測試項目的啟動路徑下, 創建一個img文件夾

    傳遞給裝飾器的圖片,在運行測試前可以不存在, 運行測試之後生成即可.

    當文件在報告中展示後, 想要看到原圖, 可以點擊報告中的縮略圖查看完整的截圖

六、完整項目代碼示例    

  1、項目目錄結構,如圖2

(圖2:項目目錄結構示例)

  2、測試用例text_wxwork.py

註:可能會涉及到敏感信息,所以有些case的注釋和text會用XXX代替  1 from appium import webdriver  2 import unittest  3 import time

  4 import datetime
  5 from selenium.webdriver.support.ui import WebDriverWait
  6 from selenium.webdriver.support import expected_conditions as EC
  7 from BeautifulReport import BeautifulReport
  8 import os
  9 from selenium.webdriver.common.by import By
 10 
 11 class wxworkTest(unittest.TestCase):
 12     '''企業微信-XXX移動端'''
 13     @classmethod
 14     def setUpClass(cls):
 15         ''' 所有的測試方法運行前運行,使用@classmethod裝飾器進行修飾,整個測試過程中只執行一次 '''
 16         wxwork = {
 17             'platformName': 'Android',  # android的apk
 18             'deviceName': 'WTKDU16905017501',  # 手機設備名稱,通過adb devices查看
 19             'platformVersion': '8.0',  # android系統的版本號
 20             'appPackage': 'com.tencent.wework',  # apk包名
 21             'appActivity': '.launch.LaunchSplashActivity',  # apk的啟動Activity
 22             #'unicodeKeyboard': True,  # 實現中文輸入,使用unicodeKeyboard的編碼方式來發送字符串
 23             #'resetKeyboard': True  # 將鍵盤給隱藏起來
 24         }
 25         cls.driver = webdriver.Remote("//127.0.0.1:4723/wd/hub", wxwork) # 連接appium
 26         cls.driver.implicitly_wait(10) # 隱式等待,最長等待10s
 27         # 切換到工作台
 28         cls.driver.find_element_by_xpath("//*[@text='工作台']").click()
 29         # 調用上滑方法,找到應用並進入
 30         cls.up(4, 500)
 31         time.sleep(1)
 32         cls.driver.find_element_by_xpath("//*[@text='XXX測試']").click()
 33         # 在移動端應用進入主頁
 34         cls.driver.find_element_by_xpath("//*[@text='XXX採購']").click()
 35         print("已進入XXX移動端/我的工作台")
 36 
 37     @classmethod
 38     def tearDownClass(cls):
 39         ''' 所有的測試方法運行後運行,使用@classmethod裝飾器進行修飾,整個測試過程中只執行一次 '''
 40         cls.driver.quit() # 退出應用
 41         print("已退出企業微信")
 42 
 43     @classmethod
 44     def getsize(self):
 45         '''定義方法:自動獲取手機屏幕'''
 46         x = self.driver.get_window_size()['width']
 47         y = self.driver.get_window_size()['height']
 48         return (x, y)
 49 
 50     @classmethod
 51     def up(self,n,t):
 52         '''定義方法:上滑手勢操作'''
 53         screensize = self.getsize()
 54         x1 = int(screensize[0] * 0.5)
 55         y1 = int(screensize[1] * 0.7)
 56         y2 = int(screensize[1] * 0.3)
 57         for i in range(0, n):
 58             self.driver.swipe(x1, y1, x1, y2, t)
 59             print("", i + 1, "次上滑成功啦")
 60 
 61     def save_img1(self):
 62         """直接指定路徑,並用當前時間生成截圖"""
 63         # 獲取當前時間,用於命名截圖
 64         pic_time = datetime.datetime.now().strftime("%Y-%m-%d %H-%M-%S")
 65         # 截圖保存寫入的路徑中
 66         self.driver.get_screenshot_as_file("E:\\08PyCharmProject\\Case1\\img\\" + pic_time + ".png")
 67 
 68     def save_img(self, img_name):
 69         '''用於放在測試報告的截圖,os.path.abspath:返回當前目錄的絕對路徑,傳入一個img_name,並存儲到指定路徑下'''
 70         self.driver.get_screenshot_as_file('{}\{}.png'.format(os.path.abspath('E:\\08PyCharmProject\\Case1\\img'), img_name))
 71 
 72     def editClear(self, text):
 73         '''定義清除文本框方法'''
 74         # 123代表光標移動到末尾
 75         self.driver.keyevent(123)
 76         for i in range(0, len(text)):
 77             self.driver.keyevent(67)  # 67退格鍵
 78         # 清空後回車
 79         self.driver.keyevent(66)
 80 
 81     def main_purchurse(self):
 82         '''定義重新進入主頁面的方法'''
 83         '''用text不管用,用xpath的話,每個用例最終停留的界面不同,所以也不行,最後忍痛決定用tap'''
 84         # 坐標點擊我的工作台
 85         self.driver.tap([(720, 1644), (1080, 1792)], 500)
 86         # 坐標點擊XXX
 87         self.driver.tap([(0, 1644),(360, 1792)],500)
 88         print("已在XXX主頁面")
 89 
 90     def detail_purchurse(self):
 91         '''定義重新進入XXX明細頁面的方法'''
 92         # 坐標點擊我的工作台
 93         self.driver.tap([(720, 1644), (1080, 1792)], 500)
 94         # 坐標點擊XXX
 95         self.driver.tap([(0, 1644), (360, 1792)], 500)
 96         # 通過定位電話圖標,點擊進入XXX明細頁面
 97         self.driver.find_element_by_xpath("//*[@text='ABs3ach']").click()
 98         print("已在XXX明細頁面")
 99 
100     def test_case1(self):
101         '''case1:我的工作台:各種類型的訂單統計跳轉功能測試'''
103         self.driver.find_element_by_xpath("//*[@text='未發貨']").click()
104         print("case1執行成功:完成了工作台的所有跳轉測試")
124 
125     @BeautifulReport.add_test_img('test_case2') # 在測試用例中掛載一個裝飾器,報錯自動截圖
126     def test_case2(self):
127         '''case2:我的工作台/XXX/XXX,來回切換(報錯自動截圖貼報告,不報錯時用時間命名的截圖,不貼報告)'''
128         self.main_purchurse() # 調用方法:我的工作台切換到XXX
129         # XXX切換到XXXX:tap定位
130         self.driver.tap([(360, 1644), (720, 1792)], 500)
131         print("成功切換到XXXX模塊")
132         self.save_img1() # 截圖斷言彈窗提示語,沒報錯則調用另一個方法截圖,沒有入參 另一個方法不會附在測試報告上
133         print("case2執行成功:完成底部大模塊的切換測試")150 
151     @BeautifulReport.add_test_img('test_case4') #報錯自動截圖
152     def test_case4(self):
153         '''case4:XXX主表:待下單狀態的功能校驗(報錯自動截圖貼報告,不報錯也截圖,不貼報告)'''
154         self.main_purchurse() # 調用方法:我的工作台切換到XXX
155         # 搜索功能測試:回車鍵搜索
156         self.driver.find_element_by_xpath("//android.widget.EditText[@text='供應商/採購員']").send_keys("M0001")
157         self.driver.keyevent(66) # 回車鍵搜索
158         print("搜索成功")
159         # 獲取文本框的文本,用於傳參給editClear函數
160         get_text = self.driver.find_element_by_xpath("//android.widget.EditText[@text='供應商/採購員']").get_attribute('text')
161         # 再次點擊文本框,用於清空操作
162         self.driver.find_element_by_xpath("//android.widget.EditText[@text='供應商/採購員']").click()
163         self.editClear(get_text) # 調用方法:清空文本框的值
164         # 獲取清空後文本框的文本,用於判斷是否刪除成功
165         get_text1 = self.driver.find_element_by_xpath("//android.widget.EditText[@text='供應商/採購員']").get_attribute("text")
166         print((get_text1))
167         if get_text1 == "供應商/採購員":
168             print("文本框刪除成功")
169         else:
170             print("文本框刪除失敗")
171         # 點擊分享
172         self.driver.find_element_by_xpath("//*[@text='分享']").click()
173         print("您想要分享給微信好友,還是企業微信好友呢?")
174         self.save_img('test_case4') # 不報錯也自動截圖
175         self.driver.find_element_by_xpath("//*[@text='取消']").click()
176         print("取消分享")
177         print("case4執行成功:完成XXX-待下單狀態的功能測試")
178 
179     def test_case5(self):
180         '''case5:XXX明細頁的狀態切換測試'''
181         self.detail_purchurse() # 調用方法:進入XXX明細頁
182         # 點擊採購中tab列表,因為text有時不起作用,保險起見這裡用了兄弟元素定位
183         self.driver.find_element_by_xpath("//android.widget.Button[@text='一鍵操作']/../android.view.View[4]//*[contains(@text,'採購中')]").click()
184         print("已進入XXX明細頁的採購中列表")
185         '''這裡刪除了一些不重要的'''193         
       print("case5執行成功:完成版料明細頁的狀態切換") 194 195 def test_case6(self): 196 '''case6:XXX明細頁:待下單功能校驗1-搜索區''' 197 self.detail_purchurse() # 調用方法:進入XXX明細頁 198 # 搜索框輸入sku,回車搜索 199 self.driver.find_element_by_xpath("//*[@class='android.widget.EditText']").send_keys("F00000583") 200 self.driver.keyevent(66) 201 print("sku搜索成功") 202 # 獲取文本框的文本,用於傳參給editClear函數 203 get_text = self.driver.find_element_by_xpath("//*[@class='android.widget.EditText']").get_attribute('text') 204 print(get_text) 205 # 再次點擊文本框,用於清空操作 206 self.driver.find_element_by_xpath("//*[@class='android.widget.EditText']").click() 207 self.editClear(get_text) # 調用方法:清空文本框的值 208 # 獲取清空後文本框的文本,用於判斷是否刪除成功 209 get_text1 = self.driver.find_element_by_xpath("//*[@class='android.widget.EditText']").get_attribute("text") 211 print((get_text1)) 212 if get_text1 == "供應商/採購單號/物料名稱/sku/設計款號/採購員": 213 print("文本框刪除成功") 214 else: 215 print("文本框刪除失敗") 216 # 目標交期排序功能測試 217 self.driver.find_element_by_xpath("//*[@text='目標交期']").click() 218 print("目標交期篩選成功") 219 # 篩選功能測試(兄弟元素定位) 220 self.driver.find_element_by_xpath("//android.widget.Button[@text='一鍵操作']/../android.widget.Button").click() 221 self.driver.find_element_by_xpath("//android.widget.Button[@text='超期']").click()
225 self.driver.find_element_by_xpath("//android.widget.Button[@text='重置']").click() 226 self.driver.find_element_by_xpath("//android.widget.Button[@text='超期']").click() 227 self.driver.find_element_by_xpath("//android.widget.Button[@text='確定']").click() 228 print("成功輸入超期篩選條件") 229 print("搜索成功") 230 # 一鍵操作功能測試:確認採購 231 self.driver.find_element_by_xpath("//android.widget.Button[@text='一鍵操作']").click() 232 # 定位複選框,點擊確認採購:兄弟元素定位,因為有多個android.view.View兄弟,所以要帶上[] 233 self.driver.find_element_by_xpath("//android.view.View[contains(@text,'地址')]/../android.view.View[1]/android.view.View[1]").click() 234 print("勾選上啦") 235 # 確認採購,雖然有text,但沒定位成功,所以這裡用的是ia+class組合定位 236 self.driver.find_element_by_xpath("//*[@resource-id='container']/android.widget.Button[3]").click() 237 print("確認採購成功") 238 self.driver.find_element_by_xpath("//*[@text='完成']").click() 239 print("case6執行成功:完成搜索與一鍵確認採購測試") 240 241 def test_case7(self): 242 '''case7:XXX明細頁:待下單的功能校驗2-卡片區(不報錯時也截圖)''' 243 self.detail_purchurse() # 調用方法:進入XXX明細頁 244 # 點擊放大圖片,方法一:從最父級,一層一層往下找 245 self.driver.find_element_by_xpath("//*[@resource-id='container']/android.view.View[5]/android.view.View[1]/android.view.View[1]/android.view.View[1]/android.view.View[4]/android.widget.Image").click() 246 print("放大圖片") 247 self.save_img('test_case7') # 調用截圖方法 248 time.sleep(2) 249 self.driver.keyevent(4) # 返回鍵 250 # 點擊放大圖片,方法二:根據兄弟元素(text:地址)定位到自己,再往下找 251 # 根據兄弟元素定位到自己,再往下找 252 self.driver.find_element_by_xpath("//android.view.View[contains(@text,'地址')]/../android.view.View/android.widget.Image").click() 253 self.driver.keyevent(4) 254 print("再次放大圖片") 255 print("case7執行成功:完成卡片區的圖片放大功能測試") 256 257 def test_case8(self): 258 '''case8:XXX明細頁:採購中狀態-通知發貨功能測試''' 259 self.detail_purchurse() # 調用方法:進入XXX明細頁 260 # 點擊採購中tab列表,因為text有時不起作用,保險起見這裡用了兄弟元素定位 261 self.driver.find_element_by_xpath("//android.widget.Button[@text='一鍵操作']/../android.view.View[4]//*[contains(@text,'採購中')]").click() 262 print("已進入XXX明細頁的採購中列表") 263 # 點擊通知發貨 264 self.driver.find_element_by_xpath("//*[@text='通知發貨']").click() 265 print("已進入發貨窗口") 266 # 打印本次採購單價輸入框的文本 267 get_text = self.driver.find_element_by_xpath("//*[contains(@text,'*本次採購單價')]/../android.view.View/android.widget.EditText").get_attribute('text') 268 print(get_text) 269 # 定位到本次採購單價,並清除文本框的值,重新輸入新的採購單價 270 self.driver.find_element_by_xpath("//*[contains(@text,'*本次採購單價')]/../android.view.View/android.widget.EditText").click() 271 self.editClear(get_text) 272 self.driver.find_element_by_xpath("//*[contains(@text,'*本次採購單價')]/../android.view.View/android.widget.EditText").send_keys(1) 273 # 打印是否免付選擇框的文本 274 text = self.driver.find_element_by_xpath("//*[contains(@text,'*是否免付')]/../android.view.View/android.view.View").text 275 print(text) 276 # 點擊是否免付選擇框 277 self.driver.find_element_by_xpath("//*[contains(@text,'*是否免付')]/../android.view.View/android.view.View").click() 278 print("成功彈窗") 279 # 方法一:彈窗默認為是免付,直接確定 280 self.driver.find_element_by_xpath("//android.view.View[contains(@text,'確定')]").click() 281 # 方法二:再次點擊出現免付彈窗,向上滑動,選到否免付後,確定 282 self.driver.find_element_by_xpath("//*[contains(@text,'*是否免付')]/../android.view.View/android.view.View").click() 283 self.up(2, 500) 284 loc = ("xpath", "//android.view.View[contains(@text,'確定')]") 285 try: 286 # 顯示等待,EC.presence_of_element_located()直到元素出現 287 e = WebDriverWait(self.driver, 3, 0.5).until(EC.presence_of_element_located(loc)) 288 # 也可以用By 289 #WebDriverWait(self.driver, 3, 0.5).until(EC.presence_of_element_located(By.XPATH, "//android.view.View[contains(@text,'確定')]")) 290 # 也可以用lambda函數 291 #e = WebDriverWait(self.driver, 3 ,0.5).until(lambda x: self.driver.find_element_by_xpath("//android.view.View[contains(@text,'確定')]")) 292 e.click() 293 print("成功選擇非免付啦") 294 except: 295 pass 296 # 上傳圖片 297 self.driver.find_element_by_xpath("//android.view.View[contains(@text,'添加圖片')]").click() 298 self.driver.find_element_by_xpath("//*[@resource-id='com.tencent.wework:id/aqa'][contains(@text,'其他方式')]").click() 299 self.driver.find_element_by_xpath("//*[@resource-id='com.android.documentsui:id/dir_list']/android.widget.LinearLayout[1]").click() 300 print("成功上傳圖片") 301 # 確定發貨 302 self.driver.find_element_by_xpath("//android.widget.Button[contains(@text,'確定')]").click() 303 print("發貨成功") 304 305 @unittest.skip("暫時不執行該用例") 306 def test_case9(self): 307 '''case9:訂單明細頁:測試''' 308 309 @BeautifulReport.add_test_img('test_case90') 310 def test_case90(self): 311 '''用於看測試報告的報錯截圖''' 312 self.main_purchurse() # 調用方法:進入XXX主表 313 self.driver.find_element_by_xpath("111") 314 print("case90:故意輸入錯誤的定位,用於報錯截圖")

  3、執行用例excute_case.py

 1 import unittest
 2 from BeautifulReport import BeautifulReport
 3 import datetime
 4 
 5 if __name__ == '__main__':
 6     # 加載測試用例:指定E:\\08PyCharmProject\\Case1\\test_case目錄,「test*.py」匹配指定目錄下所有test開頭的.py文件
 7     test_suite = unittest.defaultTestLoader.discover('E:\\08PyCharmProject\\Case1\\test_case', pattern='test*.py')
 8     # 獲取當前時間,用於命名測試報告標題
 9     now = datetime.datetime.now().strftime('%Y-%m-%d %H-%M-%S')
10     # 將用例加到對象中
11     result = BeautifulReport(test_suite)
12     # report方法實現了用例的執行、用例執行結束的結果統計、生成測試報告等操作
13     # :filename -> 測試報告名稱, 如果不指定默認文件名為report.html,description -> 測試報告用例名稱展示,report_dir-> 報告文件寫入路徑
14     result.report(filename='測試報告'+str(now), description='自動化測試', report_dir='E:\\08PyCharmProject\\Case1\\report')

  4、執行結束後的img和report展示

  1)img展示,如圖3

 

(圖3:img目錄展示截圖)

  2)report展示,如圖4-6

(圖4:report目錄展示測試報告)

 

 

 (圖5:html測試報告1)

 

(圖6:html測試報告2-詳細數據,報錯的用例會有截圖)