Appium+python自动化(三十二)- 代码写死一时爽,框架重构火葬场 - PageObject+unittest(超详解)

简介

江湖有言:”代码写死一时爽,框架重构火葬场“,更有人戏言:”代码动态一时爽,一直动态一直爽😂“,虽然听起来有点耸人听闻,但也没有想象中的那么严重,我们在开发写代码的时候留心和注意就可以了。

为了重构时,少掉些头发,在开发的时候就得注意了。

写死代码后,有变动后出现bug后我们的反应

 

大佬和菜鸟对遗留写死代码的反应

最后和宏哥一起膜拜一下能够重构写死代码的大牛

是不是有宏哥的风范啊

闲话少说,进入今天的主题:PageObject+unittest。

问题思考

前面我们都是基于线性模型来编写测试脚本,而且元素定位方式和属性值都是写死的。在业务场景简单的情况下这样写无可厚非,但是一旦遇到产品需求变更,业务逻辑比较复杂需要维护的时候就非常麻烦了,那么该如何应对这种情况呢?

场景案例

结合前面我们所学,测试考研帮App登录场景,按照线性模型来构造出脚本如下: 考研帮登录测试场景

kyb_login.py

代码实现

参考代码
# coding=utf-8
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2019-8-16
@author: 北京-宏哥   QQ交流群:707699217
Project:学习和使用appium自动化测试-代码写死一时爽,框架重构火葬场 - PageObject+unittest
'''
# 3.导入模块
from appium import webdriver
import yaml
from selenium.common.exceptions import NoSuchElementException
import logging
import logging.config


CON_LOG='../log/log.conf'
logging.config.fileConfig(CON_LOG)
logging=logging.getLogger()


stream=open('../yaml/desired_caps.yaml','r')
data=yaml.load(stream)

desired_caps={}
desired_caps['platformName']=data['platformName']

desired_caps['platformVersion']=data['platformVersion']
desired_caps['deviceName']=data['deviceName']

desired_caps['app']=data['app']
desired_caps['noReset']=data['noReset']

desired_caps['unicodeKeyboard']=data['unicodeKeyboard']
desired_caps['resetKeyboard']=data['resetKeyboard']

desired_caps['appPackage']=data['appPackage']
desired_caps['appActivity']=data['appActivity']

driver = webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub', desired_caps)

def check_updateBtn():
    logging.info("check_updateBtn")

    try:
        element = driver.find_element_by_id('android:id/button2')
    except NoSuchElementException:
        logging.info('update element is not found!')
    else:
        element.click()


def check_skipBtn():
    logging.info("check_skipBtn")
    try:
        element = driver.find_element_by_id('com.tal.kaoyan:id/tv_skip')
    except NoSuchElementException:
        logging.info('skipBtn element is not found!')
    else:
        element.click()

check_updateBtn()
check_skipBtn()

logging.info('start login...')

driver.find_element_by_id('com.tal.kaoyan:id/login_email_edittext').send_keys('自学网2018')
driver.find_element_by_id('com.tal.kaoyan:id/login_password_edittext').send_keys('zxw2018')
driver.find_element_by_id('com.tal.kaoyan:id/login_login_btn').click()
logging.info('login finished')

案例分析

上面的脚本看似都比较完善,有了log采集,参数配置、启动时页面元素自动检测。但是也存在一些不足之处:

  • 公共模块和业务模块混合在一起显得代码冗余等
  • 测试场景单一(如果要实现如下测试场景该怎么办?)
  • 元素定位属性和代码混杂在一起

以上这些都是需要优化的地方。

测试场景

操作步骤

预期结果

多账号登录

不同的用户名密码来进行登录

能够正常登录

异常登录

用户名或者密码错误、或者为空进行登录,

登录失败,同时界面要给出相应的提示

注册

点击注册,然后进行注册信息填写

能够注册成功

重构优化思路

  • 将一些公共的内容(如:check_updateBtn,check_skipBtn,capability)抽离出来。
  • 元素定位方法和元素属性值与业务代码分离
  • 登录功能模块封装为一个独立的模块
  • 使用unittest进行用例综合管理

Page Object

Page Object是Selenium自动化测试项目开发实践的最佳设计模式之一,通过对界面元素的封装减少冗余代码,同时在后期维护中,若元素定位发生变化,只需要调整页面元素封装的代码,提高测试用例的可维护性。

脚本实现

封装App启动配置信息

desired_caps.py

代码实现

参考代码
# coding=utf-8
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2019-8-16
@author: 北京-宏哥   QQ交流群:707699217
Project:学习和使用appium自动化测试-代码写死一时爽,框架重构火葬场 - PageObject+unittest
'''
# 3.导入模块
from appium import webdriver
import yaml
import logging
import logging.config

CON_LOG='../log/log.conf'
logging.config.fileConfig(CON_LOG)
logging=logging.getLogger()

def appium_desired():
    file = open('../yaml/desired_caps.yaml', 'r')
    data = yaml.load(file)

    desired_caps={}
    desired_caps['platformName']=data['platformName']
    desired_caps['platformVersion']=data['platformVersion']
    desired_caps['deviceName']=data['deviceName']

    desired_caps['app']=data['app']
    desired_caps['appPackage']=data['appPackage']
    desired_caps['appActivity']=data['appActivity']
    desired_caps['noReset']=data['noReset']

    desired_caps['unicodeKeyboard']=data['unicodeKeyboard']
    desired_caps['resetKeyboard']=data['resetKeyboard']

    logging.info('start app...')
    driver=webdriver.Remote('http://'+str(data['ip'])+':'+str(data['port'])+'/wd/hub',desired_caps)
    driver.implicitly_wait(8)
    return driver

if __name__ == '__main__':
    appium_desired()

记得在原来的yaml配置表desired_caps.yaml补充如下内容:

unicodeKeyboard: True 

resetKeyboard: True

封装基类: 

baseView.py

代码实现

参考代码
# coding=utf-8
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2019-8-16
@author: 北京-宏哥   QQ交流群:707699217
Project:学习和使用appium自动化测试-代码写死一时爽,框架重构火葬场 - PageObject+unittest
'''
# 3.定义类
class BaseView(object):
    def __init__(self,driver):
        self.driver=driver

    def find_element(self,*loc):
        return self.driver.find_element(*loc)

封装通用公共类

common_fun.py

代码实现

参考代码
# coding=utf-8
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2019-8-16
@author: 北京-宏哥   QQ交流群:707699217
Project:学习和使用appium自动化测试-代码写死一时爽,框架重构火葬场 - PageObject+unittest
'''
# 3.导入模块
from page_object.baseView import BaseView
from page_object.desired_caps import appium_desired
from selenium.common.exceptions import NoSuchElementException
import logging
from selenium.webdriver.common.by import By

class Common(BaseView):
    cancelBtn=(By.ID,'android:id/button2')
    skipBtn=(By.ID,'com.tal.kaoyan:id/tv_skip')

    def check_cancelBtn(self):
        logging.info('==========check_cancelBtn=========')
        try:
            cancelBtn = self.driver.find_element(*self.cancelBtn)
        except NoSuchElementException:
            logging.info('no cancelBtn')
        else:
            cancelBtn.click()

    def check_skipBtn(self):
        logging.info('=========check skipBtn=============')

        try:
            skipBtn = self.driver.find_element(*self.skipBtn)
        except NoSuchElementException:
            logging.info('no skipBtn')
        else:
            skipBtn.click()

if __name__ == '__main__':
    driver=appium_desired()
    com=Common(driver)
    com.check_cancelBtn()
    com.check_skipBtn()

封装登录操作 

loginView.py

代码实现

参考代码
# coding=utf-8
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2019-8-16
@author: 北京-宏哥   QQ交流群:707699217
Project:学习和使用appium自动化测试-代码写死一时爽,框架重构火葬场 - PageObject+unittest
'''
# 3.导入模块
import logging
from page_object.common_fun import Common
from page_object.desired_caps import appium_desired
from selenium.webdriver.common.by import By

class LoginView(Common):
    username_type=(By.ID,'com.tal.kaoyan:id/login_email_edittext')
    password_type=(By.ID,'com.tal.kaoyan:id/login_password_edittext')
    loginBtn=(By.ID,'com.tal.kaoyan:id/login_login_btn')

    def login_action(self,username,password):
        self.check_cancelBtn()
        self.check_skipBtn()

        logging.info('============login_action==============')
        logging.info('username is:%s' %username)
        self.driver.find_element(*self.username_type).send_keys(username)

        logging.info('password is:%s'%password)
        self.driver.find_element(*self.password_type).send_keys(password)

        logging.info('click loginBtn')
        self.driver.find_element(*self.loginBtn).click()
        logging.info('login finished!')

if __name__ == '__main__':
    driver=appium_desired()
    l=LoginView(driver)
    l.login_action('北京-宏哥-2019','bjhg2019')

unittest用例封装

测试场景

使用如下账号进行分别登录测试

用户名

密码

自学网2018

zxw2018

自学网2017

zxw2017

666

222

Tips必备基础知识:Selenium自动化第六章-unittest单元测试框架

1.封装用例启动结束时的配置: 

myunit.py

代码实现

参考代码
# coding=utf-8
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2019-8-16
@author: 北京-宏哥   QQ交流群:707699217
Project:学习和使用appium自动化测试-代码写死一时爽,框架重构火葬场 - PageObject+unittest
'''
# 3.导入模块
import unittest
from page_object.desired_caps import appium_desired
import logging
from time import sleep

class StartEnd(unittest.TestCase):
    def setUp(self):
        logging.info('=====setUp====')
        self.driver=appium_desired()

    def tearDown(self):
        logging.info('====tearDown====')
        sleep(5)
        self.driver.close_app()

2.用例封装 

test_login.py

代码实现

参考代码
# coding=utf-8
# 1.先设置编码,utf-8可支持中英文,如上,一般放在第一行

# 2.注释:包括记录创建时间,创建人,项目名称。
'''
Created on 2019-8-16
@author: 北京-宏哥   QQ交流群:707699217
Project:学习和使用appium自动化测试-代码写死一时爽,框架重构火葬场 - PageObject+unittest
'''
# 3.导入模块
from unittest.myunit import StartEnd
from page_object.loginView import LoginView
import unittest
import logging

class TestLogin(StartEnd):

    def test_login_bjhg2019(self):
        logging.info('======test_login_bjhg-2019=====')
        l=LoginView(self.driver)
        l.login_action('北京宏哥-2018','bjhg-2019')

    def test_login_bjhg2018(self):
        logging.info('======test_login_bjhg-2018=====')
        l=LoginView(self.driver)
        l.login_action('北京宏哥-2018','bjhg-2018')

    def test_login_error(self):
        logging.info('======test_login_error=====')
        l = LoginView(self.driver)
        l.login_action('6666', '222')

if __name__ == '__main__':
    unittest.main()

小结

1.代码运行流程图

  • 11
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值