PageObject

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

什么是PageObject

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

怎么做

将各个功能的代码封装为一个类,后面要用他时,直接调用即可

代码

代码

封装App启动配置信息(desired_caps.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import yaml
import logging.config
from appium import webdriver

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

def appium_desired():

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']

logging.info('start run 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()
封装基类(baseView.py)
1
2
3
4
5
6
class BaseView(object):
def __init__(self,driver):
self.driver=driver

def find_element(self,*loc):
return self.driver.find_element(*loc)
封装通用公共类(common_fun.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
from appium_advance.page_object.baseView import BaseView  #导入刚刚封装的基类
from selenium.common.exceptions import NoSuchElementException
import logging
from selenium.webdriver.common.by import By
from appium_advance.page_object.desired_caps import appium_desired #导入刚刚封装的app启动信息

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:
element = self.driver.find_element(*self.cancelBtn)
except NoSuchElementException:
logging.info('update element is not found!')
else:
logging.info('click cancelBtn')
element.click()

def check_skipBtn(self):
logging.info("==========check_skipBtn===========")
try:
element = self.driver.find_element(*self.skipBtn)
except NoSuchElementException:
logging.info('skipBtn element is not found!')
else:
logging.info('click skipBtn')
element.click()

if __name__ == '__main__':
driver=appium_desired()
com=Common(driver)
com.check_updateBtn()
com.check_skipBtn()
封装登录操作(loginView.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import logging
from appium_advance.page_object.common_fun import Common #导入刚刚封装的通用公共类
from appium_advance.page_object.desired_caps import appium_desired
from selenium.webdriver.common.by import By

class LoginView(Common): #继承common->BaseView
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() #调用继承的common的check_cancelBtn()方法
self.check_skipBtn() #调用继承的common的check_skipBtn()方法

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

logging.info('input password:%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('name123','pwd123')

unittest用例封装

封装用例启动结束时的配置(myunit.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import unittest
from appium_advance.page_object.desired_caps import appium_desired #导入刚刚封装的app启动信息
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()
测试用例封装(test_login.py)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from  appium_advance.unittest.myunit import StartEnd
from appium_advance.page_object.loginView import LoginView
import unittest
import logging

class TestLogin(StartEnd):
def test_login_zxw2018(self):
logging.info('=========test_login_zxw2018============')
l=LoginView(self.driver)
l.login_action('name2018','pwd2018')


def test_login_zxw2017(self):
logging.info('==========test_login_zxw2017========')
l=LoginView(self.driver)
l.login_action('name2017','pwd2017')


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


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

_1.注意test开头 2.运行顺序一个test_xxx->setUp->tearDown->另一个test_xxx->…,test_xxx的顺序为xxx的默认排序,并非像线性一样从上到下的运行。_

逻辑图

iz8g6e.png