app自動化測試---自動化測試框架搭建


1.安裝依賴以及項目的基本目錄

# 安裝依賴
pip install pytest       
pip install appium-python-client   
pip install openpyxl                     # excel文件處理
pip install pytest-html                  # 測試報告

 

 

 

 

2.pom解析

pom 設計的核心思想 就是將不同的頁面單獨進行維護,在做自動化的過程中,如果前端頁面進行更改,原來寫自動化代碼就可能不再適用,因為前端頁面更改了之后,元素定位已經不再適合,自動化用例執行失敗。就要重新更改代碼,比較麻煩。

pom 將頁面與測試用例單獨封裝,頁面上的每個操作都單獨封裝起來,測試用例只需要調用封裝好的方法即可。如果頁面有改動。只需要改頁面中封裝的操作即可。

下面以登錄場景為例,編寫自動化:

conftest.py

from appium import webdriver
import pytest
import os
chromedriver= os.path.join(os.path.dirname(os.path.abspath(__file__)),'drivers/chrome/75.0.3770.140/chromedriver.exe')


@pytest.fixture(scope='session')
def driver():
    desired_caps = {
        'platformName': 'Android',                    # 測試Android系統
        'platformVersion': '7.1.2',                   # Android版本 可以在手機的設置中關於手機查看
        'deviceName': '127.0.0.1:62001',              # adb devices 命令查看  設置為自己的設備
        'automationName': 'UiAutomator2',             # 自動化引擎
        'noReset': False,                             # 不要重置app的狀態
        'fullReset': False,                           # 不要清理app的緩存數據
        'chromedriverExecutable': chromedriver,       # chromedriver 對應的絕對路徑
        'appPackage': "org.cnodejs.android.md",       # 應用的包名
        'appActivity': ".ui.activity.LaunchActivity"  # 應用的活動頁名稱(appium會啟動app的加載頁)
    }
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_capabilities=desired_caps)
    driver.implicitly_wait(5)  # 全局的隱式等待時間

    yield driver  # 將driver 傳遞出來

    driver.quit()

pom/basePage.py

"""
  所有的頁面都繼承這個類,獲得driver
"""
import time
from appium.webdriver.webdriver import WebDriver
from selenium.common.exceptions import NoSuchElementException


class BasePage:
    def __init__(self,driver:WebDriver):
        self.driver = driver

    # 獲取toast的文本值
    @property
    def result_text(self):
        try:
            toast = self.driver.find_element_by_xpath('//android.widget.Toast')
            return  toast.text
        except NoSuchElementException:
            return "找不到這個元素,請檢查自己的自動化代碼"

pom/loginPage.py

"""
  登陸頁面
"""
import time

from appium.webdriver.webdriver import WebDriver

from pom.basePage import BasePage


class LoginPage(BasePage):

    # 初始化類的時候,打開登陸頁面
    def __init__(self,driver:WebDriver):
        super(LoginPage,self).__init__(driver)
        # 判斷是否是登陸頁面
        current_activity = self.driver.current_activity
        if ".ui.activity.LoginActivity" in current_activity:
            pass
        else:
            # 不是登陸頁面,則調用方法,打開登陸頁面
            self.__go_login_page()


    # 導航到loginPage(登陸頁面),定義一個私有的方法
    def __go_login_page(self):
        # 清空app的登陸狀態(如果已經登陸,則去掉登陸狀態)
        self.driver.reset()
        # 打開首頁
        self.driver.start_activity(app_package='org.cnodejs.android.md',app_activity='.ui.activity.MainActivity')

        toggle_but = self.driver.find_element_by_android_uiautomator('resourceId("org.cnodejs.android.md:id/toolbar").childSelector(new UiSelector().className("android.widget.ImageButton"))')
        toggle_but.click()
        time.sleep(1)

        # 點擊頭像,去登陸頁面
        avatar = self.driver.find_element_by_android_uiautomator('text("點擊頭像登錄").resourceId("org.cnodejs.android.md:id/tv_login_name")')
        avatar.click()


    # 使用token的方式進行登錄
    def with_token_login(self,token):
        self.driver.find_element_by_id('org.cnodejs.android.md:id/edt_access_token').send_keys(token)
        loginbtn = self.driver.find_element_by_android_uiautomator('text("登錄").resourceId("org.cnodejs.android.md:id/btn_login")')
        # 點擊登陸
        loginbtn.click()

    # 登陸失敗的斷言
    @property
    def with_token_failed_text(self):
        # 1. 截圖
        ele = self.driver.find_element_by_id('org.cnodejs.android.md:id/edt_access_token')
        png = ele.screenshot_as_base64
        # 2. TODO 調用 ocr 圖片識別 將圖片中文字識別出來

        return ""

    # 掃碼登陸
    def with_code_login(self):
        pass


    # 使用github登陸
    def with_github_login(self):
        pass

testcases/test_user.py

from pom.loginPage import LoginPage

# 登陸的測試用例
# 使用conftest.py 中定義的 driver
def test_login(driver):
    # 打開登錄頁面
    loginpage = LoginPage(driver)
    # 使用token進行登錄
    loginpage.with_token_login('d1563473-1f0d-4307-9774-6c2ff49c93ab')

    # 登陸成功,驗證totas文本值
    result = loginpage.result_text
    assert result == "登錄成功"

啟動appium , 執行   pytest testcases\test_user.py -s -v  ,查看運行結果:

(登陸成功)

 (登陸失敗)

 

下面以登錄后發帖場景為例,編寫自動化:

pom/homePage.py

"""
  首頁
"""
from pom.basePage import BasePage
from pom.createTopicPage import CreateTopicPage


class HomePage(BasePage):

    def __init__(self,driver):
        super(HomePage,self).__init__(driver)
        # 判斷一下,是否是首頁
        if '.ui.activity.MainActivity' in self.driver.current_activity:
            pass
        else:
            self.__go_home_page()


    # 打開首頁
    def __go_home_page(self):
        self.driver.start_activity(app_activity='.ui.activity.LaunchActivity')


    # 去發帖頁面
    def go_create_topic(self):
        # 判斷是否已經到達創建話題頁面
        while not '.ui.activity.CreateTopicActivity' in self.driver.current_activity:
            # 再重新點擊一下
            create_btn = self.driver.find_element_by_android_uiautomator(
                '.resourceId("org.cnodejs.android.md:id/fab_create_topic")')
            create_btn.click()

        return CreateTopicPage(self.driver)

pom/createTopicPage.py

"""
  發帖頁面
"""
from pom.basePage import BasePage


class CreateTopicPage(BasePage):

    # 發布話題
    def create_new_topic(self,tab,title,content):
        # 選擇類型
        spinner = self.driver.find_element_by_android_uiautomator('.resourceId("org.cnodejs.android.md:id/spn_tab")')
        spinner.click()
        tab_selcotor = f'.resourceId("android:id/text1").text("{tab}")'
        self.driver.find_element_by_android_uiautomator(tab_selcotor).click()

        # 輸入標題
        title_content = self.driver.find_element_by_android_uiautomator(
            'resourceId("org.cnodejs.android.md:id/edt_title")')
        title_content.send_keys(title)

        # 輸入內容
        content_area = self.driver.find_element_by_android_uiautomator(
            'resourceId("org.cnodejs.android.md:id/edt_content")')
        content_area.send_keys(content)

        # 點擊發送
        send_btn = self.driver.find_element_by_android_uiautomator(
            'resourceId("org.cnodejs.android.md:id/action_send")')
        send_btn.click()

testcases/test_topics.py

from pom.homePage import HomePage
from pom.loginPage import LoginPage


# 發帖的測試用例
def test_create_topic(driver):
    loginpage = LoginPage(driver)
    # 用戶登錄成功
    loginpage.with_token_login('d1563473-1f0d-4307-9774-6c2ff49c93ab')

    # 首頁打開
    hp = HomePage(driver)
    # 進入創建話題頁面
    create_page = hp.go_create_topic()

    create_page.create_new_topic(tab='分享',title='123',content='哈哈哈哈哈哈')
    result = create_page.result_text
    # 根據發帖結果做斷言
    assert result == "標題要求10字以上"

啟動appium , 執行   pytest testcases\test_topics.py -s -v  ,查看運行結果:

也可以執行 pytest,查看登陸,和發帖2個測試用例的執行結果:

 

 

3. Excel數據驅動

testdata/data.xlsx

utils/file_handler.py

"""
  登陸測試用例的數據驅動化測試
"""
import pytest

from pom.loginPage import LoginPage
from utils.file_handler import FileHandler

fl = FileHandler()
# 從Excel文件中獲取數據
data = fl.get_data_by_sheet('用戶登錄')

class TestDdtLogin:

    @pytest.mark.parametrize('token,status,expect_val',data)
    def test_login(self,driver,token,status,expect_val):
        # 打開登錄頁面
        loginpage = LoginPage(driver)
        # 使用token進行登錄
        loginpage.with_token_login(token)
        if status == '成功':
            # 登錄成功, 驗證toast的文本值為登錄成功
            result = loginpage.result_text
            assert result == expect_val
        if status == "失敗":
            result = loginpage.with_token_failed_text
            assert result == expect_val

testcases/test_ddt/test_ddt_login.py

"""
  登陸測試用例的數據驅動化測試
"""
import pytest

from pom.loginPage import LoginPage
from utils.file_handler import FileHandler

fl = FileHandler()
# 從Excel文件中獲取數據
data = fl.get_data_by_sheet('用戶登錄')

class TestDdtLogin:

    @pytest.mark.parametrize('token,status,expect_val',data)
    def test_login(self,driver,token,status,expect_val):
        # 打開登錄頁面
        loginpage = LoginPage(driver)
        # 使用token進行登錄
        loginpage.with_token_login(token)
        if status == '成功':
            # 登錄成功, 驗證toast的文本值為登錄成功
            result = loginpage.result_text
            assert result == expect_val
        if status == "失敗":
            result = loginpage.with_token_failed_text
            assert result == expect_val

啟動appium , 執行   pytest testcases\test_ddt\test_ddt_login.py -s -v  ,查看運行結果:

 

 

4.測試報告

pytest-html https://pypi.org/project/pytest-html/

pytest-allure https://pypi.org/project/allure-pytest/

測試報告一:pytest-html

main.py

"""
  項目運行文件,並添加測試報告
"""
import pytest
import os,time

if __name__ == '__main__':
    report_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),'reports')
    if not os.path.exists(report_dir):
        os.mkdir(report_dir)

    report = time.strftime('%Y_%m_%d_%H_%M_%S')

    reportfile = os.path.join(report_dir,report+'.html')

    pytest.main(['testcases','-s','-v',f'--html={reportfile}'])

運行main.py 文件,Run main.py 執行即可,會執行 testcases 里面所有的測試用例

執行結束之后,生成的測試報告,我們可以在瀏覽器中打開

 

 

 5.優化conftest.py

   增加  每個測試用例執行完成之后,如果執行失敗截圖,截圖的名稱為測試用例名稱+時間格式  的相關處理

from appium import webdriver
import pytest
import os, time
chromedriver= os.path.join(os.path.dirname(os.path.abspath(__file__)),'drivers/chrome/75.0.3770.140/chromedriver.exe')

# scope='session' 標記的方法執行域為---->所有測試用例運行之前/之后 運行的方法
@pytest.fixture(scope='session',autouse=True)
def driver():
    desired_caps = {
        'platformName': 'Android',                    # 測試Android系統
        'platformVersion': '7.1.2',                   # Android版本 可以在手機的設置中關於手機查看
        'deviceName': '127.0.0.1:62001',              # adb devices 命令查看  設置為自己的設備
        'automationName': 'UiAutomator2',             # 自動化引擎
        'noReset': False,                             # 不要重置app的狀態
        'fullReset': False,                           # 不要清理app的緩存數據
        'chromedriverExecutable': chromedriver,       # chromedriver 對應的絕對路徑
        'appPackage': "org.cnodejs.android.md",       # 應用的包名
        'appActivity': ".ui.activity.LaunchActivity"  # 應用的活動頁名稱(appium會啟動app的加載頁)
    }
    driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_capabilities=desired_caps)
    driver.implicitly_wait(5)  # 全局的隱式等待時間
    yield driver  # 將driver 傳遞出來
    driver.quit()


# 該方法是用來獲取測試用例執行的結果(passed / FAILED)
@pytest.hookimpl(tryfirst=True,hookwrapper=True)
def pytest_runtest_makereport(item, call):
    outcome = yield
    rep = outcome.get_result()               # 獲取用例的執行結果
    print('用例的執行結果rep---->',rep)
    setattr(item, "rep_" + rep.when, rep)    # 將執行結果保存到 item 屬性中 , req.when 執行時


# scope='function' 標記的方法執行域為---->每個測試用例運行之前/之后 運行的方法
@pytest.fixture(scope='function',autouse=True)
def case_run(driver:webdriver,request):   # request 為 pytest_runtest_makereport 方法獲取到的執行結果(固定參數和用法)
    yield
    # 每個測試用例執行完成之后,如果執行失敗截圖,截圖的名稱為測試用例名稱+時間格式
    if request.node.rep_call.failed:
        screenshots = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'screenshots')
        if not os.path.exists(screenshots):
            os.mkdir(screenshots)
        casename: str = request.node.nodeid
        # print("執行測試用例的名字:", casename)
        # 測試用例的名字很長 testcases/test_ddt/test_ddt_login.py::TestDdtLogin::test_login[....]
        # 對字符串進行截取,截取之后顯示為  test_ddt_login-TestDdtLogin
        casename = casename[casename.rfind('/')+1:casename.rfind('::')].replace('.py::','-')
        filename = casename + '-' + time.strftime('%Y_%m_%d-%H_%M_%S')  +".png"
        screenshot_file = os.path.join(screenshots, filename)
        # 保存截圖
        driver.save_screenshot(screenshot_file)

 

 

 6. 多設備連接,並行執行測試代碼

   通過代碼的方式自動的獲取連接的多個設備,拿到設備的串號,啟動多個appium,通過多進程的方式,在多個設備上並行的執行自動化測試用例

    app自動化測試----多設備並行運行   點擊查看優化后的代碼

 

最后,執行 pip freeze > requirements.txt   ,將項目中使用的第三方包的包名和版本導出到文件中

                   pip install -r requirements.txt    # 安裝requirements.txt文件夾中庫及對應版本


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM