有以下常用的模块可以套用WEB自动化和APP自动化

【自总结Python自动化】之自动化测试框架套用模板(WEB、APP、接口)

 

 

 

 

# 主要模块

-- 项目名称
    -- page_object
        -- data
        -- locator
        -- page
        -- test(自己备用的,可以忽略)
        -- test_case
        -- utils

二、创建启动APP或WEB文件

1、启动app.py


from appium import webdriver
from Appium_20210407.pageObject.page.main_page import MainPage
from Appium_20210407.pageObject.utils.functions import Functions as Fun


class
APP(object): appData = Fun().getYamlData('app') caps = appData['caps'] server = appData['server'] ip = server['ip'] port = server['port'] def startApp(self): """启动APP""" self.driver = webdriver.Remote(f"http://{self.ip}:{self.port}/wd/hub", self.caps) self.driver.implicitly_wait(15) return self def stopApp(self): """关闭APP""" self.driver.quit() def goto_main(self): """跳转主页""" return MainPage(self.driver)

①获取package和activity:

adb shell dumpsys activity | grep LAUNCHER

②app对应caps参数:

caps:
  platformName: "android"
  deviceName: "emulator-5554"
  appPackage: "com.tencent.wework"
  appActivity: ".launch.LaunchSplashActivity"
  noReset: True
  skipServerInstallation: True        # 跳过UIautomator2 server安装
  skipDeviceInitialization: True      # 跳过设备的初始化
#  waitForIdleTimeout: 1
#  automationName: "UiAutomator2"      # Toast内容
#  dontStopAppOnReset: True            # 测试之前不停止app运行
server:
  ip: 127.0.0.1
  port: 4723

③对应的方法文件


import os
import yaml

class
Functions: def pathUp(self): """获取上级路径""" path = os.path.dirname(os.path.dirname(__file__)) return path def getYaml(self,path): """获得yaml数据""" with open(path,encoding='utf-8') as file: data = yaml.load(file) return data def getCommonYaml(self): """获得公共yaml数据""" commonPath = self.pathUp()+ "/data/common.yaml" return self.getYaml(commonPath) def getYamlData(self,data): """获取当前的yaml数据""" yamlPath = self.pathUp() + self.getCommonYaml()[data] return self.getYaml(yamlPath)

2、启动web.py


from selenium import webdriver
from page_object.page.login_page import LoginPage


class
Web: def startWeb(self): """开启WEB自动化""" self.driver = webdriver.Chrome("XXX/XXX/Administration_UI_Automate_Code/page_object/utils/chromedriver") self.driver.get("https://recycle_dev.XXXXXX.cn:9700/#/login") self.driver.implicitly_wait(10) return self def stopWeb(self): """关闭浏览器""" self.driver.quit() def goto_loginPage(self): """跳转登录页""" return LoginPage(self.driver)

①对应的方法文件:


import os
import time
import pyautogui
import pyperclip
import yaml
from pykeyboard import PyKeyboard
from pymouse import PyMouse

class
Functions: def upPath(self): """获取上级路径""" path = os.path.dirname(os.path.dirname(__file__)) return path def getData(self, path): """获取yaml数据""" with open(path, 'r', encoding='utf-8') as file: data = yaml.load(file) return data def getYamlPath(self): """获取公共不同yaml路径""" path = self.upPath() + '/data/common.yaml' return self.getData(path) def getYamlData(self, yamlName): """获取当前yaml数据""" path = self.upPath() + self.getYamlPath()[yamlName] return self.getData(path) def getIndex(self,type,name): """获取区级对应角标index""" list = self.getYamlData('selectData')[type] for index,value in enumerate(list): if name in value: return index+1 def upload_file(self, path): """PyUserInput方法""" # 创建鼠标对象 k = PyKeyboard() # 创建键盘对象 m = PyMouse() filepath = "/" # 模拟快捷键Command+Shift+G k.press_keys(["Command", "Shift", "G"]) # 输入文件路径 x_dim, y_dim = m.screen_size() m.click(x_dim // 2, y_dim // 2, 1) # 复制文件路径开头的斜杠/ pyperclip.copy(filepath) # 粘贴斜杠/ k.press_keys(["Command", "V"]) time.sleep(2) # 输入文件全路径进去 k.type_string(path) fileName = '机构信息-批量导入模板 (9).xls' pyperclip.copy(fileName) k.press_keys(["Command", "V"]) time.sleep(2) k.press_key("Return") time.sleep(2) k.press_key("Return") time.sleep(2) def upload_file2(self,path): """pyautogui方法【不用】""" filepath = "/" pyautogui.press('shiftleft') pyautogui.hotkey("Command", "Shift", "G") pyautogui.typewrite(path, interval=0.25) pyautogui.press('return') time.sleep(2) pyautogui.press('return') time.sleep(2)

②对应的方法文件:

import os
import yaml


class Functions:

    def curPath(self):
        """获取本层目录"""
        basePath = os.path.dirname(os.path.abspath(__file__))
        return basePath

    def upPath(self):
        """获取上级目录路径"""
        basePath = os.path.dirname(os.path.dirname(__file__))
        return basePath

    def getYamlData(self,yamlName):
        """获取yaml数据"""
        yamlPath = self.upPath() + f'/data/{yamlName}Data.yaml'
        with open(yamlPath,encoding='utf-8') as file:
            data = yaml.load(file,Loader=yaml.FullLoader)
        return data[yamlName]

 

三、创建base_page文件

1、app自动化base


from appium.webdriver.webdriver import WebDriver

class
Page: def __init__(self,driver:WebDriver): self.driver = driver def find_element(self,loc): """查找单元素""" return self.driver.find_element(*loc) def find_elements(self,loc): """查找多元素""" return self.driver.find_elements(*loc) def el_sendKeys(self,loc,text): """输入事件""" self.find_element(loc).send_keys(text) def el_click(self,loc): """点击事件""" self.find_element(loc).click() def swipeUp(self,loc): """上滑操作""" size = self.driver.get_window_size() width = size['width'] height = size['height'] while(True): try: # ele = self.driver.find_element(MobileBy.XPATH,loc) ele = self.find_element(loc) return ele except: print("继续上滑") self.driver.swipe(0.5 * width, 0.7 * height, 0.5 * width, 0.3 * height)

2、web自动化base

from selenium.webdriver.chrome.webdriver import WebDriver
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.action_chains import ActionChains as AC


class Base:

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

    def find_element(self,loc):
        """查找元素"""
        return self.driver.find_element(*loc)

    def find_elements(self,loc):
        """查找多组元素"""
        return self.driver.find_element(*loc)

    def el_click(self,loc):
        """点击事件"""
        self.webDriverWait(loc).click()

    def el_sendKeys(self,loc,text):
        """输入事件"""
        self.webDriverWait(loc).send_keys(text)

    def webDriverWait(self,loc):
        """显式等待,查找元素"""
        WebDriverWait(self.driver,10).until(EC.visibility_of_element_located(loc))
        return self.find_element(loc)

    def handles(self):
        """获取所有页面handles"""
        return self.driver.window_handles

    def enter_window(self,index):
        """进入页面"""
        handles = self.handles()
        self.driver.switch_to.window(handles[index-1])

    def get_text(self,loc):
        """获取文字内容"""
        return self.webDriverWait(loc).text

    def move_to_element(self,loc):
        """移动鼠标到元素上"""
        AC(self.driver).move_to_element(self.webDriverWait(loc)).perform()

    def el_clear(self,loc):
        """清空数据"""
        self.webDriverWait(loc).clear()

    def el_clear_sendKeys(self,loc,text):
        """清空数据并输入数据"""
        self.webDriverWait(loc).clear()
        self.el_sendKeys(loc,text)

四、关联每个页面跳转

1、举例web自动化

①跳转到登录页

class Web:

    def startWeb(self):
        """开启WEB自动化"""
 

    def stopWeb(self):
        """关闭浏览器"""
        

    def goto_loginPage(self):
        """跳转登录页"""
        return LoginPage(self.driver)

②登录页跳转到首页

from page_object.locator.loginPage_loc import LoginPageLoc as loc
from page_object.page.base_page import Page
from page_object.page.main_page import MainPage
from page_object.utils.functions import Functions as Fun


class LoginPage(Page):
    """登录页"""

    loginData = Fun().getYamlData("login")

    def login_action(self):
        """登录操作"""
        self.el_sendKeys(loc.telephone_loc, self.loginData['telephone'])
        self.el_sendKeys(loc.password_loc, self.loginData['password'])
        self.el_click(loc.loginButton_loc)

    def goto_mainPage(self):
        """跳转首页"""
        self.login_action()
        return MainPage(self.driver)

③首页跳转到信息列表页面

from page_object.page.base_page import Page
from page_object.page.organList_page import OrganListPage
from page_object.locator.mainPage_loc import MainMenuLoc as loc



class MainPage(Page):
    """首页"""

    def goto_organListPage(self):
        """跳转信息列表页面"""
        self.el_click(loc.baseInfo_loc)
        self.el_click(loc.organInfo_loc)
        return OrganListPage(self.driver)

④列表页面跳转到新增页面

from page_object.locator.organListPage_loc import OrganListPageLoc as loc
from page_object.page.base_page import Page
from page_object.page.newOrgan_page import NewOrganPage
from page_object.utils.functions import Functions as Fun


class OrganListPage(Page):
    """XX信息列表页面"""

    organListData = Fun().getYamlData('organList')
    filePath = organListData['filePath']

    def goto_newOrganPage(self):
        """跳转新增XX页面"""

        # 点击新增XX按钮
        self.el_click(loc.addOrganButton_loc)
        return NewOrganPage(self.driver)

    def upload(self):
        """上传文件"""
        self.el_click(loc.batchImportButton_loc)
        self.el_click(loc.uploadOfCheckButton_loc)
        Fun().upload_file(self.filePath)

⑤新增页面

from page_object.page.base_page import Page
from page_object.locator.newOrganPage_loc import NewOrganPageLoc as loc


class NewOrganPage(Page):
    """新增机构页面"""

    def newOrgan(self):
        """新增机构"""

        # 输入XX名称
        self.el_sendKeys(loc.organName_loc, loc.organName)
        # 输入XX编码
        self.el_sendKeys(loc.organCode_loc, loc.organCode)# 输入详细地址
        self.webDriverWait(loc.fullAddress_loc).send_keys(loc.fullAddressText)
        # 请输入联系人
        self.webDriverWait(loc.contacts_loc).send_keys(loc.contactsText)
        # 请输入联系电话
        self.webDriverWait(loc.telePhone_loc).send_keys(loc.telePhoneText)
        # 请输入说明
        self.webDriverWait(loc.explain_loc).send_keys(loc.explainText)
        # 点击保存按钮
        self.el_click(loc.saveButton_loc)

五、元素定位文件和yaml数据文件

1、创建定位文件

在locator目录下,,例如:newOrganPage_loc.py

from selenium.webdriver.common.by import By
from page_object.utils.functions import Functions as Fun


class NewOrganPageLoc:

    organData = Fun().getYamlData('organ')
    provinceLevelArea = organData['provinceLevelArea']
    municipalLevelArea = organData['municipalLevelArea']
    districtLevelArea = organData['districtLevelArea']
    districtName = organData['districtName']
    organType = organData['organType']
    organTypeName = organData['organTypeName']
    addressName = organData['addressName']
    fullAddress = organData['fullAddress']
    contacts = organData['contacts']
    telePhone = organData['telePhone']
    explain = organData['explain']

    organName = organData['organName']
    organCode = organData['organCode']
    fullAddressText = organData['fullAddressText']
    contactsText = organData['contactsText']
    telePhoneText = organData['telePhoneText']
    explainText = organData['explainText']



    # XX名称
    organName_loc = (By.CSS_SELECTOR, ".formtable > form > div:nth-child(1) >div>div>div>input")
    # XX编码
    organCode_loc = (By.CSS_SELECTOR, ".formtable > form > div:nth-child(2) >div>div>div>input")
    # 单位级别
    unitLevel_loc = (By.CSS_SELECTOR, ".formtable > form > div:nth-child(3) >div>div>div>div>input")
    # 选择XX级别:区级
    selectDistrictLevel_loc = (By.XPATH, "//body/div[2]/div/div/ul/li[2]")
    # 所属行政区:省级地区
    provinceLevelArea_loc = (By.XPATH, "//*[@placeholder='{}']".format(provinceLevelArea))# XX地址:区
    districtLevel_loc = (By.XPATH, "//*[@placeholder='"+districtLevelArea[0]+"']")
    # XX地址区级:某某区
    TJDistrict2_loc = (By.XPATH, "//body/div[9]/div/div/ul/li["+ str(Fun().getIndex(f'{districtLevelArea}',f'{addressName}')) +"]")
    # 详细地址
    fullAddress_loc = (By.XPATH, f"//*[@placeholder='{fullAddress}']")
    # 联系人
    contacts_loc = (By.XPATH, f"//*[@placeholder='{contacts}']")
    # 联系电话
    telePhone_loc = (By.XPATH, f"//*[@placeholder='{telePhone}']")
    # 说明
    explain_loc = (By.XPATH, f"//*[@placeholder='{explain}']")
    # XX保存按钮
    saveButton_loc = (By.CSS_SELECTOR, ".el-button--primary")

2、相关yaml数据读取文件:

①这一种适用于下拉框的数据选择

区级地区:
  - 请选择
  - 和平区
  - 河东区
  - 河西区
  - 南开区
  - 河北区
  - 红桥区
  - 东丽区
  - 西青区
  - 津南区
  - 北辰区
  - 武清区
  - 宝坻区
  - 滨海新区
  - 宁河区
  - 静海区
  - 蓟州区
XX类型:
  - 卫生事业
  - 国家机关
  - 教育事业
  - 文化事业
  - 科技事业
  - 体育事业
  - 团体组织
  - 其他
区域:
  - 省级地区
  - 市级地区
  - 区级地区

并结合下面的方法:

    def getIndex(self,type,name):
        """获取区级对应角标index"""
        list = self.getYamlData('selectData')[type]
        for index,value in enumerate(list):
            if name in value:
                return index+1

②普通yaml格式

organName: XX51201
organCode: jg51201
provinceLevelArea: 省级地区
municipalLevelArea: 市级地区
districtLevelArea: 区级地区
districtName: 滨海新区      # 经常改的
organType: 请选择XX类型
organTypeName: 其他        # 经常改的
addressName: 蓟州区        # 经常改的
fullAddress: 请输入详细地址
fullAddressText: 蓟州区黄崖关
contacts: 请输入联系人
contactsText: zc联系人
telePhone: 请您输入手机号
telePhoneText: 136420xxxxx
explain: 请输入说明 (100字以内)
explainText: zcXX说明

六、创建测试用例

1、web自动化

①基础用例

from page_object.page.web import Web
from page_object.utils.functions import Functions as Fun

 
 class BaseTestCase:

     loginData = Fun().getYamlData("login")

     def setup(self):
        self.web = Web().startWeb(self.loginData['url'])
        self.login = self.web.goto_loginPage()
        self.organListPage = self.login.goto_mainPage().goto_organListPage()

     def teardown(self):
        # Web().stopWeb()
        self.web.stopWeb()

 

②测试用例

from page_object.page.web import Web


class TestOrgan:

    def setup(self):
        self.web = Web().startWeb()
        self.login = self.web.goto_loginPage()

    def teardown(self):
        self.web.stopWeb()
      
def test_addOrgan(self): """添加机构""" self.login.goto_mainPage()\ .goto_organListPage()\ .goto_newOrganPage()\ .newOrgan() def test_upload(self): """上传文件""" self.login.goto_mainPage()\ .goto_organListPage()\ .upload()

2、app自动化

from Appium_20210407.pageObject.page.app import APP


class TestUser:

    def setup(self):
        self.app = APP().startApp()
        self.main = self.app.goto_main()


    def teardown(self):
        self.app.stopApp()


    def test_addUser(self):
        self.main.goto_addressList().goto_addUser().addUser_action()

 

 

七、接口自动化目录模板

参考项目地址:https://github.com/Owen-ET/2021_Python_HogwartsSDE17_Project_Practice/tree/master/API

【自总结Python自动化】之自动化测试框架套用模板(WEB、APP、接口)

 

 

# 主要模块

-- 项目名称
    -- Server/API
        -- data
        -- feishu_work
            --calendar_api
                --base.py
                --feishuWorkAddress.py
        -- test_case
            --calendar
                --baseCalendarsTestData.py
                --testCalendars.py
            --base_testcase.py
        -- utils
            --functions.py

1、calendar_api接口

下面写base和增删改查等等接口

base.py

import requests

from API.utils.functions import Functions


class Base:


    def __init__(self):

        self.s = requests.Session()
        self.token = self.get_token()
        self.s.headers = {
            'Authorization': f"Bearer {self.token}",
            'Content-Type': "application/json; charset=utf-8"
        }

        self.baseUrl = self.base['calendarsUrl']
        self.list = []


    def get_token(self):
        '''获取token'''
        self.base = Functions().getYamlData('base')
        url = self.base['token']['url']
        params = self.base['token']['params']
        r = self.send('POST',url,json=params).json()
        return r['tenant_access_token']


    def send(self,*args,**kwargs):
        return self.s.request(*args,**kwargs)

feishuWorkAddress.py

from API.feishu_work.calendar_api.base import Base

class FeishuWorkCalendars(Base):


    def get_calendarsList_info(self,calendar_id):
        '''获取日历列表'''

        url = f'{self.baseUrl}{calendar_id}'
        # print(self.get_token())
        # headers = {
        #     'Authorization': f"Bearer {self.get_token()}",
        #     'Content-Type': "application/json; charset=utf-8"
        # }


        # r = self.s.get(url).json()
        r = self.send('GET',url).json()
        return r


    def create_calendars(self,summary,description,permissions,color,summary_alias):
        '''创建日历'''

        url = f'{self.baseUrl}'
        # headers = {
        #     'Authorization': f"Bearer {self.get_token()}",
        #     'Content-Type': "application/json; charset=utf-8"
        # }
        data = {
            'summary': summary,
            'description': description,
            'permissions': permissions,
            'color': color,
            'summary_alias': summary_alias
        }

        r = self.send('POST',url,json=data).json()
        return r


    def update_calendars(self,calendar_id,summary,description,permissions,color,summary_alias):
        '''修改日历'''

        url = f'{self.baseUrl}{calendar_id}'
        # headers = {
        #     'Authorization': f"Bearer {self.get_token()}",
        #     'Content-Type': "application/json; charset=utf-8"
        # }
        data = {
            'summary': summary,
            'description': description,
            'permissions': permissions,
            'color': color,
            'summary_alias': summary_alias
        }

        r = self.send('PATCH',url, json=data).json()
        return r


    def delete_calendars(self,calendar_id):
        '''删除日历'''

        url = f'{self.baseUrl}{calendar_id}'
        # headers = {
        #     'Authorization': f"Bearer {self.get_token()}",
        #     'Content-Type': "application/json; charset=utf-8"
        # }
        r = self.send('DELETE',url).json()
        return r


    def delete_error_calendar_id(self,calendar_err,calendar_id_null):
        '''清除错误日历id'''

        # 查询全部日历
        res_info = self.get_calendarsList_info(calendar_id_null)
        print(res_info)
        # 循环取出所有日历id到数组中
        for i in range(2):
            result = res_info['data']['calendar_list'][i]['calendar_id']
            self.list.append(result)
        # 清除错误日历id
        self.list.remove(calendar_err)
        return self.list[0]

2、test_case测试用例

分为三个部分:

①base用例:base_testcase.py

from API.feishu_work.calendar_api.feishuWorkAddress import FeishuWorkCalendars
from API.test_case.calendar.baseCalendarsTestData import BaseCalendarsTestData


class BaseTestCase:

    def setup_class(self):
        self.data = BaseCalendarsTestData()
        self.calendars = FeishuWorkCalendars()


    def setup(self):

        print("=======case_start=======")
        try:
            self.calendar_id = self.calendars.delete_error_calendar_id(self.data.calendar_err,self.data.calendar_id_null)
        except:
            pass


    def teardown(self):
        print("=======case_stop=======")

 

②用例数据:baseCalendarsTestData.py

from API.utils.functions import Functions


class BaseCalendarsTestData(Functions):
    baseData = Functions().getYamlData('calendars')

    summary = baseData['summary']
    newSummary = baseData['newSummary']
    description = baseData['description']
    permissions = baseData['permissions']
    color = baseData['color']
    summary_alias = baseData['summary_alias']
    calendar_err = baseData['calendar_err']
    calendar_id_null = baseData['calendar_id_null']

 

③测试用例:testCalendars.py

from API.test_case.base_testcase import BaseTestCase


class TestCalendars(BaseTestCase):


    def test_create_calendars(self):
        res_create = self.calendars.create_calendars(self.data.summary,
                                        self.data.description,
                                        self.data.permissions,
                                        self.data.color,
                                        self.data.summary_alias)
        assert res_create['code'] == 0

        result = self.calendars.get_calendarsList_info(self.calendars.delete_error_calendar_id(self.data.calendar_err,self.data.calendar_id_null))
        assert result['code'] == 0


    def test_get_calendars(self):
        res_get = self.calendars.get_calendarsList_info(self.data.calendar_id_null)
        print(res_get)
        assert res_get['code'] == 0


    def test_update_calendars(self):
        res_update = self.calendars.update_calendars(self.calendar_id,
                                                     self.data.newSummary,
                                                     self.data.description,
                                                     self.data.permissions,
                                                     self.data.color,
                                                     self.data.summary_alias)
        assert res_update['code'] == 0

        res_get = self.calendars.get_calendarsList_info(self.data.calendar_id_null)
        print(res_get)
        assert res_get['code'] == 0


    def test_delete_calendars(self):
        res_delete = self.calendars.delete_calendars(self.calendar_id)
        assert res_delete['code'] == 0

        res_info = self.calendars.get_calendarsList_info(self.calendar_id)
        assert res_info['code'] == 0
        print(res_info)

3、封装的公共方法utils

functions.py

import os
import yaml


class Functions:

    def upPath(self):
        '''获取上级目录路径'''
        basePath = os.path.dirname(os.path.dirname(__file__))
        return basePath


    def getYamlData(self,yamlName='base'):
        '''获取yaml数据'''
        yamlPath = self.upPath() + f'/data/{yamlName}Data.yaml'
        with open(yamlPath,encoding='utf-8') as file:
            data = yaml.load(file)
        return data[yamlName]

 

4、yaml数据

baseData.yaml

base:
  calendarsUrl: "https://open.feishu.cn/open-apis/calendar/v4/calendars/"
  token:
    url: 'https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal/'
    params:
      app_id: 'cli_a18de0f937f9500d'
      app_secret: 'nUzgBNrv1sX20ARjI4dXUbVMYsDkUyPp'

 

相关文章:

  • 2022-12-23
  • 2022-12-23
  • 2021-06-17
  • 2021-10-11
  • 2021-11-17
  • 2021-08-03
  • 2021-08-03
猜你喜欢
  • 2021-12-28
  • 2022-12-23
  • 2021-06-23
  • 2022-01-18
  • 2022-12-23
  • 2022-12-23
  • 2021-09-18
相关资源
相似解决方案