appium自动化测试之PO模型设计

news2024/11/15 7:50:37

 

目录

PO模型

PO分层

PO模型设计框架

config目录

common目录

pages目录

function目录

case目录

logs目录

report目录

runTest.py文件

总结:


我们在做自动化的时候应该都听过PO模型,那么什么是PO模型呢?PO模型在自动化中的作用是什么呢?

PO模型

PO其实就是:、Page Object Model,也称作为POM模型,PO其实是一种设计模式,已经在自动化测试中流行起来,以增强测试维护并减少代码重复。页面对象是面向对象的类,用作页面的接口和被测设备。 然后,只要测试需要与该页面的UI进行交互,这些测试便会使用该页面对象类的方法,其好处在于,如果页面的UI发生了更改,则无需更改测试本身,只需更改其中的代码即可。页面对象需要更改。 随后,所有支持该新UI的更改都位于一个位置。其实说到低就是一句话:把每一个页面当作一个类,把页面上的元素信息和代码操作分离开,然后方面后面我们进行管理代码和元素内容

PO分层

PO分层也就是对我们自动化代码进行分层具体可以分为以下基层

1、基础层:封装一些定位方法,点击,输入,滑动等操作

2、公用层:获取元素方法,操作元素方法,获取CMD信息等方法

3、业务层:页面元素信息。

4、逻辑层:一些功能,比如登录,注册。

5、数据层:测试信息存放地方

emmm,这里是安静这边自身的了解,当然可能每个人对PO分层的理解不同,可能大佬们分的比我这里更加详细。(一起分享,共同学习)

这里安静简单拿项目来进行实际介绍下PO内容

首先我们先看以前如何编写测试用例的

# coding:utf-8
from appium import webdriver
import time
import unittest
class login(unittest.TestCase):
    def setUp(self):
        desired_caps = {
                        'platformName': 'Android',  # 测试版本
                        'deviceName': 'emulator-5554',   # 设备名
                        'platformVersion': '5.1.1', # 系统版本
                        'appPackage': 'com.taobao.taobao', #apk的包名
                       'appActivity': 'com.ali.user.mobile.login.ui.UserLoginActivity', # apk的launcherActivity
                        'noReset':True , # 清除缓存
                        }
        self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
    def tearDown(self):
        self.driver.quit()
    def test01(self):
        '''
        账号密码错误
        '''
        self.driver.implicitly_wait(40)
        self.driver.find_element_by_id("com.eboss2.sbs:id/tv_username").send_keys("22222")
        time.sleep(2)
        self.driver.find_element_by_id("com.eboss2.sbs:id/tv_password").send_keys("33333333")
        time.sleep(2)
        self.driver.find_element_by_id("com.eboss2.sbs:id/btn_login").click()
        time.sleep(5)
        x = self.driver.find_element_by_id("com.eboss2.sbs:id/shopName_TextView").text
        print(x)
        self.assertEqual(x,'请输入正确的手机号')
if __name__ == '__main__':
    unittest.main()

相信绝大部分的同学,第一次写代码的时候都会这样写测试用例

PO模型设计框架

肯定有人会问?什么是自动化框架?自动化框架有什么好处呢?这个地方我们先不说答案,我们后面写

首先把安静这边设计的框架先整体列出来,一个个为大家分析

appium_python    # 目标工程
    
    - case        # 用例存放
        test_login.py    # 编写用例

    - common    # 公用方法
        appium_start.py    # 启动appium
        Base.py    # 封装基础内容
        dos_cmd.py    # cmd执行
        HTmlTestRunner.py    # 报告文件
        logger.py    # 日志
        read_yaml.py    # 读取yaml文件
    
    - config    # 页面元素存放
        appium.py    # login页面存放
        
    - function     # 功能点
        login.py    # 登录逻辑
        
    - logs    # 日志存放内容
        
    - pages    # 获取页面元素信息
        login_page.py    # 获取登录元素信息
        
    -report    # 报告存放地方
        
    runTest.py    # 主函数

我们这里需要这么多内容,才能完成上面的简单的操作。

config目录

这里我们主要存放一些页面元素信息,前面也写了两种方法进行封装页面元素

# appium.yaml
LoginPage:
  dec: 登录
  locators:
    -
      name: 用户名
      type: id
      value: com.taobao.taobao:id/aliuser_login_mobile_et
    -
      name: 密码
      type: android
      value: resourceId("com.taobao.taobao:id/aliuser_register_sms_code_et")
    -
      name: 登录按钮
      type: className
      value: android.widget.Button

想象下,元素信息有了,我们是不是需要进行读取元素信息。读取元素信息的时候,是不是又需要通过PO模型的方法,把每个页面的元素都列举出来

common目录

common目录中包含一些公用的部分,比如读取yaml方法,执行cmd内容,appium中常用的方法等操作

# read_yaml.py

import yaml
import os
class   GetYaml():
    def __init__(self,file_path):
        # 判断文件是否存在
        if os.path.exists(file_path):
            self.file_path = file_path
        else:
            print('没有找到%s文件路径'%file_path)
        self.data = self.read_yaml()
    def read_yaml(self):
         with open(self.file_path,'r',encoding='utf-8')as f:
            p = f.read()
            return p
    def get_data(self,key=None):
        result = yaml.load(self.data,Loader=yaml.FullLoader)
        if key == None:
            return result
        else:
            return result.get(key)

if __name__ == '__main__':
    read_yaml = GetYaml('E:/appium_python/config/appium.yaml')
    xx = read_yaml.get_data('LoginPage')
    print(xx['locators'])

pages目录

这里我们把每个类中都代表一个页面,获取页面上的所有信息

# coding:utf-8
from common.Base import BaseApp
import os
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from common.loger import Logger
path = os.path.dirname(os.path.realpath(__file__))
yaml_path = os.path.join(os.path.join(os.path.dirname(path),'config'),'appium.yaml')
class Login_element:
    def __init__(self,driver):
        self.log = Logger('element.py')
        self.driver = driver
        self.get_element = BaseApp(self.driver)
    def user_element(self):
        ''' 获取用户名元素'''
        self.log.info('正在获取用户名元素信息---------------------------------------')
        element = self.get_element.get_element(yaml_path,'LoginPage')['locators'][0]
        self.log.info('用户名元素信息为:%s'%element)
        return element

    def password_element(self):
        ''' 获取密码元素'''
        self.log.info('正在获取用户名元素信息-------------------------------------')
        element = self.get_element.get_element(yaml_path,'LoginPage')['locators'][1]
        self.log.info('密码元素信息为:%s'%element)
        return element

    def login_boot(self):
        ''' 获取登录按钮元素'''
        self.log.info('正在获取用户名元素信息-------------------------------------')
        element = self.get_element.get_element(yaml_path,'LoginPage')['locators'][2]
        self.log.info('登录按钮元素信息为:%s'%element)
        return element

    def toast(self,message):
        '''获取toast信息'''
        toast_loc = ("xpath", ".//*[contains(@text,'%s')]"%message)
        element = WebDriverWait(self.driver, 30, 0.1).until(EC.presence_of_element_located(toast_loc)).text
        return element

页面用例元素都已经全部获取出来了,那么我们可以通过封装一些操作内容,比如说登录,注册,然后直接把我们的数据存放进去

function目录

function目录表示每个测试点,例如、;登录和注册全部都单独封装起来,用的时候,可以直接进行调用

# login.py

# coding:utf-8
from pages.login_page2 import Login_element
class LoginTest:
    def __init__(self,driver):
        self.element = Login_element(driver)
        self.app = self.element.get_element

    def login(self,username,password):
        self.app.send_text(self.element.user_element(),username)
        self.app.send_text(self.element.password_element(),password)
        self.app.click(self.element.login_boot())

case目录

case表示存放测试用例的目录

# test_login.py

from function.login import LoginTest
from common.appium_start import start
import unittest
import threading
import time
from common.loger import Logger
import warnings
warnings.simplefilter("ignore", ResourceWarning)
class BaseDriver(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        '''启动apk'''
        cls.log = Logger('anjing')
        cls.log.info('app启动中')
        cls.driver = start()
        cls.log.info('app启动完成')
        cls.login = LoginTest(cls.driver)
    def test01(self):
        '''账号密码错误'''
        self.log.info('用例名称:账号密码错误,测试数据:账号名:11111,密码:22222,')
        self.login.login('11111','22222')
        element= self.login.element.toast('手机号')
        self.log.info('test01获取toast信息为:%s'%element)
        self.assertEqual(element,'请输入正确的手机号')

    def test02(self):
        '''账号密码错误1'''
        self.log.info('用例名称:账号密码错误1,测试数据:账号名:222,密码:33333,')
        self.login.login('2222','33333')
        element= self.login.element.toast('手机号')
        self.log.info('test02获取toast信息为:%s' %element)
        self.assertEqual(element,'请输入正确的手机号')

    @classmethod
    def tearDownClass(cls):
        '''退出APK'''
        cls.driver.quit()

if __name__ == '__main__':
    t1 = threading.Thread(target=start)
    t1.start()
    time.sleep(20)
    t2 = threading.Thread(target=unittest.main())
    t2.start()   

logs目录

logs表示执行用例过程中,存放自己打印的日志地方和appium日志

report目录

report表示存放测试报告的位置

runTest.py文件

这个主执行文件,用来执行所有的用例,生成测试报告,发送邮件。

# coding:utf-8
import unittest
from common import HTMLTestRunner_cn
import time
import os
import smtplib
import threading
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from common.appium_start import start
import warnings
warnings.simplefilter("ignore", ResourceWarning)
# 脚本路径
path = os.path.dirname(os.path.realpath(__file__))
# case用例
case_path = os.path.join(path,'case')
# 执行用例 返回discover
def add_case(rule="test*.py"):
    '''加载所有的测试用例'''
    # 如果不存在这个case文件夹,就自动创建一个
    if not os.path.exists(case_path):os.mkdir(case_path)
    # 定义discover方法的参数
    discover = unittest.defaultTestLoader.discover(case_path,
                                                  pattern=rule,
                                                  top_level_dir=None)
    return discover

# 执行报告
def run_case(discover):
    report_path =  os.path.join(path,'report')
    now = time.strftime("%Y-%m-%d-%H-%M-%S")  # 最新的报告
    report_abspath = os.path.join(report_path, now+"result.html")  # 报告的位置
    rp=open(report_abspath,"wb")
    runner=HTMLTestRunner_cn.HTMLTestRunner(rp,
                                        title=u"测试报告",
                                        description=u"用例的执行情况")
    runner.run(discover)
    return report_abspath


def sen_mail(file_path):
    smtpserver = 'smtp.163.com'
    # 发送邮箱用户名密码
    user = 'xxxxxx@163.com'
    password = 'xxxxxx'
    # 发送邮箱
    sender = 'xxxxxx@163.com'
    # 接收邮箱
    receiver ='821006052@qq.com'
    #导入报告
    with open(file_path, "rb") as fp:
        mail_body = fp.read()
    msg=MIMEMultipart()
    body=MIMEText(mail_body,_subtype="html",_charset="utf-8")
    msg['Subject']=u'自动化测试报告'
    msg['from']=sender # 发送邮件
    msg['to']=receiver # 接手邮件
    msg.attach(body)
    att = MIMEText(mail_body, "base64", "utf-8")   # 生成附件
    att["Content-Type"] = "application/octet-stream"
    att["Content-Disposition"] = 'attachment; filename="report.html"' # 生成附件名称
    msg.attach(att)
    smtp = smtplib.SMTP()
    smtp.connect(smtpserver)   # 连接服务器
    smtp.login(user,password)  # 登录服务器
    # 发送邮件  split(',')分隔符
    smtp.sendmail(sender, receiver.split(','), msg.as_string())     # 关闭
    print ("邮件发送")

def main():
     discover = add_case() # 调用执行 执行用例
     file_path = run_case(discover) # 用例生成报告
     # sen_mail(file_path) # 发送报告

if __name__=="__main__":
    # add_case()
    t1 = threading.Thread(target=start)
    t1.start()
    time.sleep(20)
    t2 = threading.Thread(target=main)
    t2.start()

整体的PO模型设计都已经理完了,相信大家肯定都会有一种体验,怎么感觉原始的方法比较简单,代码还少,还简单,但是如果测试用例较多呢?那么是不是觉得这样方法就很简单,很已读。

这里我们在回复前面留下的那个问题? 自动化框架有什么用?

如果自动化框架建立起来了,那么组里一些代码基础比较弱的同学,都可以自己进行按照一个模板进行编写测试用例。如果页面元素或者UI发生了变化,我们只需要找到对应的page和元素信息进行修改,这样就可以继续保持以前的用例了。

那么当中PO模型也占了很大的用途,很清楚的使我们代码更加简洁,已读。也方便维护。

通过上面的内容,相信大家对PO模型有了简单的了解,也相信大家都有不同的分层,这里可以留言一起进行讨论,学习更多方便简单的方法

总结:

感谢每一个认真阅读我文章的人!!!

 我个人整理了我这几年软件测试生涯整理的一些技术资料,包含:电子书,简历模块,各种工作模板,面试宝典,自学项目等。欢迎大家点击下方名片免费领取,千万不要错过哦。

   Python自动化测试学习交流群:全套自动化测试面试简历学习资料获取点击链接加入群聊【python自动化测试交流】:http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DhOSZDNS-qzT5QKbFQMsfJ7DsrFfKpOF&authKey=eBt%2BF%2FBK81lVLcsLKaFqnvDAVA8IdNsGC7J0YV73w8V%2FJpdbby66r7vJ1rsPIifg&noverify=0&group_code=198408628

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/729266.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【链表OJ】删除链表中重复的结点

⭐️ 往期链表相关OJ 💫链接1:链表分割 💫链接2:链表中倒数第k个结点(快慢指针问题) 💫链接3:leetcode 876.链表的中间结点(快慢指针问题) 💫链接4:leetcode 206.反转链表 &#x1…

第二章React全家桶之面向组件编程

文章目录 一、组件的基本理解和使用1-1、函数式组件1-2、类式组件 二、组件实例的三大核心属性2-1、state的基本使用2-2-1、state的总结 2-2、props的基本使用2-2-1、props的传值与批量传值2-2-2、对props进行限制2-2-3、props的简写2-2-4、类式组件中的构造器与props2-2-5、函…

使用 eKuiper 按需桥接 CAN Bus 数据至 MQTT

CAN Bus 是一种广泛应用于汽车和工业领域的通信协议,它能够让多个设备在同一网络中进行交互。而 MQTT 是一种广泛应用于物联网领域的通信协议,作为一种轻量级的发布-订阅消息传输协议,它有效地促进了机器之间的通信。 通过将 CAN Bus 数据桥…

Vue组件库Element-常见组件-分页

常见组件-Pagination 分页 Pagination 分页&#xff1a;当数据过多时&#xff0c;会使用分页分解数据 具体关键代码如下&#xff1a;&#xff08;重视注释&#xff09; <template><div><!-- Pagination 分页 --><el-pagination background layout"…

Etsy店铺被封的原因是什么?如何防封

ETSY是一个全球知名的在线市场和电商平台&#xff0c;专注于手工艺品、独特商品和创意艺术。它为卖家提供了一个平台来展示和销售自己的手工制品、艺术品、珠宝、家居用品、时尚配饰等各种创意产品。作为一个颇受中国商家青睐的平台&#xff0c;Etsy在账号检测方面也是不亚于亚…

微软MFC技术中消息的分类

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;今天来聊聊MFC技术中消息的分类。 微软Windows中的消息虽然很多&#xff0c;但是种类并不繁杂&#xff0c;大体上有3种&#xff1a;窗口消息、命令消息和控件通知消息。 窗口消息 窗口消息是系统中最为常见…

网络安全(黑客)自学路线

一.零基础学习 在网络安全的学习过程中&#xff0c;基础知识是一个绕不过的问题&#xff0c;Web知识本身就非常丰富&#xff0c;覆盖范围也非常广泛。 首先是大家比较熟悉的浏览器、数据库、服务器&#xff1b; 以及由简到难的HTML、JavaScript和CSS、PHP、Java、.net&#…

【Flutter】使用 Drift 实现 Flutter 数据持久化

文章目录 一、前言二、版本信息三、Drift 简介四、如何安装和设置 Drift五、基础使用1. 创建数据库和表2. 插入、查询、更新和删除数据3. 使用事务 六、总结 一、前言 你是否渴望成为 Flutter 的专家&#xff0c;掌握更多的技巧和最佳实践&#xff1f;我们有个好消息要告诉你&…

【vue3】学习笔记--组件通信方式

学习vue3总是绕不开vue2 vue2组件通信方式总结&#xff1a; 1、props&#xff1a;可以实现父子组件&#xff0c;子父组件&#xff0c;甚至兄弟组件通信 2、自定义事件&#xff1a;实现子父组件通信 3、全局事件总线$bus:可以实现任意组件通信 4、pubsub&#xff1a;发布订阅模…

目标检测常用的评价指标

目标检测常用的评价指标 1 IoU&#xff08;Intersection over Union&#xff09;2 GIoU&#xff08;Generalized IoU&#xff09;3 DIoU&#xff08;Distance-IoU&#xff09;4 CIoU&#xff08;Complete-IoU&#xff09;5 EIoU&#xff08;Efficient-IoU&#xff09;6 SIoU7 W…

爬虫入门07——requests中携带cookie信息

爬虫入门07——requests中携带cookie信息 对于需要登陆的网站如果不携带cookie是无法获取我们所需内容的就以查看我在CSDN中的订单为例&#xff0c;在登陆后可以查看到订单信息 而当我们使用Python代码发出请求时&#xff0c;是不携带cookie&#xff0c;因此无法拿到订单相关信…

Flink的状态是否支持任务间共享

背景&#xff1a; 在日常编写代码的过程中&#xff0c;我们经常会在方法内部new很多的其他类对象来进行编码工作&#xff0c;那么对于这种情况怎么让new出来的对象是一个我们特意创建出来的一个mock实例&#xff0c;从而让我们能完全控制new出来的对象的所有行为呢&#xff1f…

【雕爷学编程】Arduino动手做(154)---AFMotor电机扩展板模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

数模混合项目:模拟跨数字走线注意事项

数模混合项目中&#xff0c;模拟在数字上走线是常有的事&#xff0c;这里需要注意几个点: 1.模拟电源在数字上走线影响不大&#xff0c;但尽量走top metal和AP层。 2.模拟高频线&#xff0c;尤其是时钟&#xff0c;尽量不要在数字上走线&#xff0c;非要走&#xff0c;最好下…

数据总线学习

为啥要数据总线 使用服务化方式发布&#xff0c;业务端和中间件完全解耦合。一处生产&#xff0c;处处消费设计理念。提供用户可定制的托管化通用消费方案&#xff08;如同步mysql到缓存&#xff0c;同步mysql到es&#xff0c;消费mysql到大数据等托管服务&#xff09; 特性 …

python configparser模块常用方法以及常见报错处理

configparser 是 Python 中一个用于处理配置文件的标准库&#xff0c;可以帮助你生成、读取和修改配置文件的内容 1. 生成配置文件 import configparser# 创建一个配置文件对象 config configparser.ConfigParser()# 添加配置项和值 config[Section1] {key1: value1, key2: …

java 科学计算库 Smile

官网 https://haifengl.github.io/ github https://haifengl.github.io/ 简介 统计机器智能和学习引擎&#xff0c;或者简称 Smile&#xff0c;是一个有前途的现代机器学习系统&#xff0c;在某些方面类似于 Python 的 scikit-learn。它是用 Java 开发的&#xff0c;也提供…

私域账号防范手册

微信为什么要养号吗&#xff1f;为什么会被封&#xff1f;是什么原理&#xff0c;怎么解封&#xff0c;这些你的了解吗&#xff1f; 来看看这篇文章&#xff0c;这些都能给你解答。

向日葵远程命令执行漏洞(CNVD-2022-10270) 漏洞复现

为方便您的阅读&#xff0c;可点击下方蓝色字体&#xff0c;进行跳转↓↓↓ 01 漏洞描述02 影响范围03 利用方式05 实战案例06 修复方案 01 漏洞描述 向日葵远程控制是一款面向企业和专业人员的远程pc管理和控制的服务软件。可以在任何有网络的情况下&#xff0c;轻松访问并控制…

Linux命令之nc命令

一、命令简介 nc是netcat的简写&#xff0c;是一个功能强大的网络工具&#xff0c;有着网络界的瑞士军刀美誉。nc命令在linux系统中实际命令是ncat&#xff0c;nc是软连接到ncat。nc命令的主要作用如下&#xff1a; 实现任意TCP/UDP端口的侦听&#xff0c;nc可以作为server以T…