app自动化06

PO模式

Page Object Model

测试页面和测试脚本分离,即页面封装成类,供测试脚本进行调用。
也就是说,脚本只需要负责关心流程,具体操作某个元素的步骤封装在页面对应的类的方法中

优缺点

优点
1.提高测试用例的可读性;
2.减少了代码的重复;
3.提高测试用例的可维护性,特别是针对UI频繁变动的项目;
缺点
结构复杂: 基于流程做了模块化的拆分。

项目需求

- 更多-移动网络-首选网络类型-点击2G
- 更多-移动网络-首选网络类型-点击3G
- 显示-搜索按钮-输入hello-点击返回

文件目录

PO模式
- scripts
- - test_settting.py
- pytest.ini

代码

test_setting.py
import time
from appium import webdriver


class TestSetting:

    def setup(self):
        # server 启动参数
        desired_caps = {}
        # 设备信息
        desired_caps['platformName'] = 'Android'
        desired_caps['platformVersion'] = '5.1'
        desired_caps['deviceName'] = '192.168.56.101:5555'
        # app的信息
        desired_caps['appPackage'] = 'com.android.settings'
        desired_caps['appActivity'] = '.Settings'
        # 解决输入中文
        desired_caps['unicodeKeyboard'] = True
        desired_caps['resetKeyboard'] = True

        # 声明我们的driver对象
        self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

    def test_mobile_network_2g(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'更多')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'移动网络')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'首选网络类型')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'2G')]").click()

    def test_mobile_network_3g(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'更多')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'移动网络')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'首选网络类型')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'3G')]").click()

    def test_mobile_display_input(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'显示')]").click()
        self.driver.find_element_by_id("com.android.settings:id/search").click()
        self.driver.find_element_by_id("android:id/search_src_text").send_keys("hello")
        self.driver.find_element_by_class_name("android.widget.ImageButton").click()

    def teardown(self):
        self.driver.quit()
pytest.ini
[pytest]
# 添加行参数
addopts = -s --html=./report/report.html
# 文件搜索路径
testpaths = ./scripts
# 文件名称
python_files = test_*.py
# 类名称
python_classes = Test*
# 方法名称
python_functions = test_*

多文件区分测试用例

需求

- 使用多个文件来区分不同的测试页面

好处

-修改不同的功能找对应的文件即可

步骤

1. 在scripts下新建test_network.py文件
2. 在scripts下新建test_dispaly.py文件
3. 移动不同的功能代码到对应的文件
4. 移除原有的test_setting.py文件

文件目录

PO模式
- scripts
- - test_network.py
- - test_dispaly.py
- pytest.ini

代码

test_network.py
from appium import webdriver


class TestNetwork:

    def setup(self):
        # server 启动参数
        desired_caps = {}
        # 设备信息
        desired_caps['platformName'] = 'Android'
        desired_caps['platformVersion'] = '5.1'
        desired_caps['deviceName'] = '192.168.56.101:5555'
        # app的信息
        desired_caps['appPackage'] = 'com.android.settings'
        desired_caps['appActivity'] = '.Settings'
        # 解决输入中文
        desired_caps['unicodeKeyboard'] = True
        desired_caps['resetKeyboard'] = True

        # 声明我们的driver对象
        self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

    def test_mobile_network_2g(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'更多')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'移动网络')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'首选网络类型')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'2G')]").click()

    def test_mobile_network_3g(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'更多')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'移动网络')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'首选网络类型')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'3G')]").click()

    def teardown(self):
        self.driver.quit()
test_display.py
from appium import webdriver


class TestDisplay:

    def setup(self):
        # server 启动参数
        desired_caps = {}
        # 设备信息
        desired_caps['platformName'] = 'Android'
        desired_caps['platformVersion'] = '5.1'
        desired_caps['deviceName'] = '192.168.56.101:5555'
        # app的信息
        desired_caps['appPackage'] = 'com.android.settings'
        desired_caps['appActivity'] = '.Settings'
        # 解决输入中文
        desired_caps['unicodeKeyboard'] = True
        desired_caps['resetKeyboard'] = True

        # 声明我们的driver对象
        self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)

    def test_mobile_display_input(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'显示')]").click()
        self.driver.find_element_by_id("com.android.settings:id/search").click()
        self.driver.find_element_by_id("android:id/search_src_text").send_keys("hello")
        self.driver.find_element_by_class_name("android.widget.ImageButton").click()

    def teardown(self):
        self.driver.quit()

封装前置代码

需求

- 将前置代码进行封装

好处

- 前置代码只需要写一份

步骤

1. 新建base文件夹
2. 新建base_driver.py文件
3. 新建函数init_driver
4. 写入前置代码并返回
5. 修改测试文件中的代码

文件目录

PO模式
- base
- - base_driver.py
- scripts
- - test_network.py
- - test_dispaly.py
- pytest.ini

代码

base_driver.py
from appium import webdriver

def init_driver():
    # server 启动参数
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    # app的信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'
    # 解决输入中文
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True
    # 声明我们的driver对象
    return webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
test_network.py
from base.base_driver import init_driver


class TestNetwork:

    def setup(self):
        self.driver = init_driver()

    def test_mobile_network_2g(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'更多')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'移动网络')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'首选网络类型')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'2G')]").click()

    def test_mobile_network_3g(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'更多')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'移动网络')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'首选网络类型')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'3G')]").click()

    def teardown(self):
        self.driver.quit()
test_display.py
from base.base_driver import init_driver


class TestDisplay:

    def setup(self):
        self.driver = init_driver()

    def test_mobile_display_input(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'显示')]").click()
        self.driver.find_element_by_id("com.android.settings:id/search").click()
        self.driver.find_element_by_id("android:id/search_src_text").send_keys("hello")
        self.driver.find_element_by_class_name("android.widget.ImageButton").click()

    def teardown(self):
        self.driver.quit()

分离测试脚本

需求

  • 测试脚本只剩流程
  • 其他的步骤放倒page类中

好处

  • 测试脚本只专注过程
  • 过程改变只需要修改脚本
  • 增加可维护性

步骤

1. 新建page文件夹
2. 新建network_page.py文件
3. 新建display_page.py文件
4. 调用init函数传入driver
5. init进入需要测试的页面
6. page中新建“小动作”函数
7. 移动代码
8. 修改测试文件中的代码

文件目录

PO模式
- base
- - base_driver.py
- page
- - network_page.py
- - display_page.py
- scripts
- - test_network.py
- - test_dispaly.py
- pytest.ini

代码

base_driver.py
from appium import webdriver

def init_driver():
    # server 启动参数
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    # app的信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'
    # 解决输入中文
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True

    # 声明我们的driver对象
    return webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
network_page.py
class NetWorkPage:

    def __init__(self, driver):
        self.driver = driver
        self.driver.find_element_by_xpath("//*[contains(@text,'更多')]").click()
        self.driver.find_element_by_xpath("//*[contains(@text,'移动网络')]").click()

    def click_first_network(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'首选网络类型')]").click()

    def click_2g(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'2G')]").click()
        
    def click_3g(self):
        self.driver.find_element_by_xpath("//*[contains(@text,'3G')]").click()
display_page.py
class DisplayPage:

    def __init__(self, driver):
        self.driver = driver
        self.driver.find_element_by_xpath("//*[contains(@text,'显示')]").click()

    def click_search(self):
        self.driver.find_element_by_id("com.android.settings:id/search").click()

    def input_text(self, text):
        self.driver.find_element_by_id("android:id/search_src_text").send_keys(text)

    def click_back(self):
        self.driver.find_element_by_class_name("android.widget.ImageButton").click()
test_network.py
import sys, os
sys.path.append(os.getcwd())

from base.base_driver import init_driver
from page.network_page import NetWorkPage


class TestNetwork:

    def setup(self):
        self.driver = init_driver()
        self.network_page = NetWorkPage(self.driver)

    def test_mobile_network_2g(self):
        self.network_page.click_first_network()
        self.network_page.click_2g()

    def test_mobile_network_3g(self):
        self.network_page.click_first_network()
        self.network_page.click_3g()

    def teardown(self):
        self.driver.quit()
test_display.py
import sys, os
sys.path.append(os.getcwd())


from base.base_driver import init_driver
from page.display_page import DisplayPage


class TestDisplay:

    def setup(self):
        self.driver = init_driver()
        self.display_page = DisplayPage(self.driver)

    def test_mobile_display_input(self):
        self.display_page.click_search()
        self.display_page.input_text("hello")
        self.display_page.click_back()

    def teardown(self):
        self.driver.quit()

抽取查找元素的特征

需求

- 将元素的特征放在函数之上

好处

- 若特征改了,流程不变,可以直接在上面修改

步骤

1. 将find_element_by_xxx改为find_element
2. 将方式和具体特征向上移动
一切目的都是为了代码简单易于维护。

文件目录

PO模式
- base
- - base_driver.py
- page
- - network_page.py
- - display_page.py
- scripts
- - test_network.py
- - test_dispaly.py
- pytest.ini

代码

base_driver.py
from appium import webdriver

def init_driver():
    # server 启动参数
    desired_caps = {}
    # 设备信息
    desired_caps['platformName'] = 'Android'
    desired_caps['platformVersion'] = '5.1'
    desired_caps['deviceName'] = '192.168.56.101:5555'
    # app的信息
    desired_caps['appPackage'] = 'com.android.settings'
    desired_caps['appActivity'] = '.Settings'
    # 解决输入中文
    desired_caps['unicodeKeyboard'] = True
    desired_caps['resetKeyboard'] = True

    # 声明我们的driver对象
    return webdriver.Remote('http://127.0.0.1:4723/wd/hub', desired_caps)
network_page.py
from selenium.webdriver.common.by import By


class NetWorkPage:

    more_button = By.XPATH, "//*[contains(@text,'更多')]"   // 元组类型
    mobile_network_button = By.XPATH, "//*[contains(@text,'移动网络')]"
    click_first_network_button = By.XPATH, "//*[contains(@text,'首选网络类型')]"
    network_2g_button = By.XPATH, "//*[contains(@text,'2G')]"
    network_3g_button = By.XPATH, "//*[contains(@text,'3G')]"

    def __init__(self, driver):
        self.driver = driver
        self.driver.find_element(self.more_button).click()
        self.driver.find_element(self.mobile_network_button).click()

    def click_first_network(self):
        self.driver.find_element(self.click_first_network_button).click()

    def click_2g(self):
        self.driver.find_element(self.network_2g_button).click()

    def click_3g(self):
        self.driver.find_element(self.network_3g_button).click()
display_page.py
from selenium.webdriver.common.by import By


class DisplayPage:

    display_button = By.XPATH, "//*[contains(@text,'显示')]"   // 元组
    search_button = By.ID, "com.android.settings:id/search"
    search_edit_text = By.ID, "android:id/search_src_text"
    back_button = By.CLASS_NAME, "android.widget.ImageButton"

    def __init__(self, driver):
        self.driver = driver
        self.driver.find_element(self.display_button).click()

    def click_search(self):
        self.driver.find_element(self.search_button).click()

    def input_text(self, text):
        self.driver.find_element(self.search_edit_text).send_keys(text)

    def click_back(self):
        self.driver.find_element(self.back_button).click()
test_network.py
import sys, os
sys.path.append(os.getcwd())

from base.base_driver import init_driver
from page.network_page import NetWorkPage


class TestNetwork:

    def setup(self):
        self.driver = init_driver()
        self.network_page = NetWorkPage(self.driver)

    def test_mobile_network_2g(self):
        self.network_page.click_first_network()
        self.network_page.click_2g()

    def test_mobile_network_3g(self):
        self.network_page.click_first_network()
        self.network_page.click_3g()

    def teardown(self):
        self.driver.quit()
test_display.py
import sys, os
sys.path.append(os.getcwd())


from base.base_driver import init_driver
from page.display_page import DisplayPage


class TestDisplay:

    def setup(self):
        self.driver = init_driver()
        self.display_page = DisplayPage(self.driver)

    def test_mobile_display_input(self):
        self.display_page.click_search()
        self.display_page.input_text("hello")
        self.display_page.click_back()

    def teardown(self):
        self.driver.quit()

抽取action

需求

- 将动作进行封装

步骤

1. 新建base_action.py文件
2. 将click、send_keys抽取到文件中

文件目录

PO模式
- base
- - base_driver.py
- - base_action.py
- page
- - network_page.py
- - display_page.py
- scripts
- - test_network.py
- - test_dispaly.py
- pytest.ini

代码

base_action.py
class BaseAction:

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

    def click(self, loc):
        self.driver.find_element(loc[0], loc[1]).click()

    def input(self, loc, text):
        self.driver.find_element(loc[0], loc[1]).send_keys(text)
network_page.py
import sys, os
sys.path.append(os.getcwd())

from selenium.webdriver.common.by import By
from base.base_action import BaseAction


class NetWorkPage(BaseAction):

    more_button = By.XPATH, "//*[contains(@text,'更多')]"
    mobile_network_button = By.XPATH, "//*[contains(@text,'移动网络')]"
    click_first_network_button = By.XPATH, "//*[contains(@text,'首选网络类型')]"
    network_2g_button = By.XPATH, "//*[contains(@text,'2G')]"
    network_3g_button = By.XPATH, "//*[contains(@text,'3G')]"

    def __init__(self, driver):
        BaseAction.__init__(self, driver)
        self.click(self.more_button)
        self.click(self.mobile_network_button)

    def click_first_network(self):
        self.click(self.click_first_network_button)

    def click_2g(self):
        self.click(self.network_2g_button)

    def click_3g(self):
        self.click(self.network_3g_button)
display_page.py
import sys, os
sys.path.append(os.getcwd())

from selenium.webdriver.common.by import By
from base.base_action import BaseAction
class DisplayPage(BaseAction):

    display_button = By.XPATH, "//*[contains(@text,'显示')]"
    search_button = By.ID, "com.android.settings:id/search"
    search_edit_text = By.ID, "android:id/search_src_text"
    back_button = By.CLASS_NAME, "android.widget.ImageButton"

    def __init__(self, driver):
        BaseAction.__init__(self, driver)
        self.click(self.display_button)

    def click_search(self):
        self.click(self.search_button)

    def input_text(self, text):
        self.input(self.search_edit_text, text)

    def click_back(self):
        self.click(self.back_button)
test_network.py
import sys, os
sys.path.append(os.getcwd())

from base.base_driver import init_driver
from page.network_page import NetWorkPage
class TestNetwork:
    def setup(self):
        self.driver = init_driver()
        self.network_page = NetWorkPage(self.driver)

    def test_mobile_network_2g(self):
        self.network_page.click_first_network()
        self.network_page.click_2g()

    def test_mobile_network_3g(self):
        self.network_page.click_first_network()
        self.network_page.click_3g()

    def teardown(self):
        self.driver.quit()
test_display.py
import sys, os

import time

sys.path.append(os.getcwd())

from base.base_driver import init_driver
from page.display_page import DisplayPage


class TestDisplay:
    def setup(self):
        self.driver = init_driver()
        self.display_page = DisplayPage(self.driver)

    def test_mobile_display_input(self):
        self.display_page.click_search()
        self.display_page.input_text("hello")
        self.display_page.click_back()
    def teardown(self):
        self.driver.quit()

增加WebDriverWait

需求

- 找控件使用WebDriverWait

好处

- 防止出现cpu卡的时候找不到

步骤

1. 自己新建find方法
2. 在系统的基础上增加WebDriverWait
文件目录
PO模式
- base
- - base_driver.py
- - base_action.py
- page
- - network_page.py
- - display_page.py
- scripts
- - test_network.py
- - test_dispaly.py
- pytest.ini

代码

base_action.py
from selenium.webdriver.support.wait import WebDriverWait


class BaseAction:

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

    def find_element(self, loc, time=10, poll=1):
        return WebDriverWait(self.driver, time, poll).until(lambda x: x.find_element(loc[0], loc[1]))
        # return self.driver.find_element(by, value)

    def find_elements(self, loc, time=10, poll=1):
        return WebDriverWait(self.driver, time, poll).until(lambda x: x.find_elements(loc[0], loc[1]))
        # return self.driver.find_elements(by, value)

    def click(self, loc):
        self.find_element(loc).click()

    def input(self, loc, text):
        self.find_element(loc).send_keys(text)

总结图:



原文地址:https://www.cnblogs.com/st998/p/13804230.html