IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> web自动化测试常用操作&封装 -> 正文阅读

[开发测试]web自动化测试常用操作&封装

? ? ? ?web自动化测试,其实就是通过代码,模拟手工测试,进行的常规操作,从而达到解放劳动力、提高效率、节省成本,那么当我们用例多了,进行的操作多了,有什么办法可以提高效率呢,接下来就介绍下web的一些常用封装和操作。


一、前置条件

? ? ? ? 基于pytest测试框架的特性,前置条件可以怎么设置呢?我们可以通过把前置条件的部分代码,放置在一个叫 conftest.py 的文件中,如下:


from pages.pages_02_login import LoginPage
from selenium import webdriver
import pytest

# 启动浏览器的前置条件
@pytest.fixture()
def get_driver():
    driver = webdriver.Chrome()
    driver.implicitly_wait(10)
    driver.maximize_window()
    yield driver
    driver.quit()

# 登录的前置条件
@pytest.fixture()
def teacher_login():
    driver = webdriver.Chrome()
    driver.implicitly_wait(10)
    driver.maximize_window()
    driver.get('https://xxxxxx.html')
    login_page = LoginPage(driver)
    login_page.login('lx@com','123456')
    yield driver
    driver.quit()

我们每次调用前置条件时,直接传入夹具函数名即可;

class TestCreatCourse:

# 调用login的前置条件

    def test_close_creat_course(self,teacher_login):

        driver = teacher_login
        create_course = Create_classPage(driver)
        create_course.loading().click_add_or_create_class().click_create_class().click_close()   #点击创建/加入课程,点击创建课程
        avatar_elem = create_course.find_element(create_course.actovar_locator)
        try:
            time.sleep(1)
            assert avatar_elem.get_attribute('title') == 'xxx'
        except AssertionError as e:
            logger.error('测试用例不通过'.format(e))
            raise e




class TestLogin:

    # 调用get_driver的前置条件

    def test_without_username_and_password(self,get_driver):
        driver = get_driver
        login_pages = LoginPage(driver)
        login_pages.loading().login('','')
        try:
            actual = ['账号不能为空','密码不能为空']
            assert login_pages.get_error_msgs() == actual
        except AssertionError as e:
            login_pages.screenshot()
            logger.error('测试用例不通过:{}'.format(e))
            raise e
另外:自动化测试优化流程
1、启动浏览器,条件反射天骄一隐形等待   ==>   driver.implicity_wait(10)
2、最大化窗口,  ==>  driver.maximize_window()
3、PO模式:(下面详细描述)
 - 把每一页的操作都封装成方法,(如登录、点击、获取文本等操作)
 - 页面 url 优化成属性(url、__init__中的driver)
 - 尽量让方法的返回值,返回一个pageobject对象,可为self,,也可为其他对象,(方便链式调用)

二、PO模式

== PO模式:(Page Object Model)PMO
把一个页面转化为对象,然后可通过调用,直接执行操作:
page.login() ==> 完成登录操作
page.clear() ==> 完成清除操作
page.load() ==> 完成页面访问操作
==  将web中的每个页面的操作,封装成一个类,
字面上的意思是;把浏览器的某页面(特征、操作)转化为代码(对象) == 》对象中有属性和方法
PO模式的本质:是对代码编写的一种封装
== PO模式有什么好处
1、可读性强 ==> 通过方法名称,表页面操作
2、可扩展性 ==> 可添加多个页面,且相对独立
3、可维护性 ==> 前端发生变化,用例发生变化,直接修改相应配置即可。
4、可复用性 ==> 重复代码可共用
==   页面操作中,返回(return)信息的操作,
 - return self  ==> 不需要返回数据,self表页面对象,以方便链式调用
 - 需要获取数据时,返回数据,如:return text、 return actual_value

三、分层设计

同时结合分层设计思想,提取元素定位表达式,放置类属性下,以便当元素定位发生变化时,不用改动多条代码,举例如下:
======   login_page.py

class LoginPages:
    host = 'xxx'       # 通过路径拼接获取,配置在py文件或yaml文件当中,使用时需导入
    url = host + '/Home/User/login.html'

    username_locator = (By.NAME, 'account')  # 用户名元素定位
    password_locator = (By.NAME, 'pass')  # 密码
    login_btn_locator = (By.XPATH, "//div[contains(@class, 'pt-login')]/a[@class='btn-btn']")  # 登录按钮
    error_msg_locator = (By.CLASS_NAME, 'error-tips')  # 错误信息弹框

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

    def login(self,account,password):
        self.driver.find_elemet(*self.username_locator).send_keys(account)  # 加上*,是因为参数为元组,有两个变量,要进行元组拆包
        self.driver.find_elemet(*self.password_locator).send_keys(password)
        self.driver.find_elemet(*self.login_btn_locator).click()
        return self   # 返回self,是方便链式调用

    def get_url(self,url):
        self.driver.get(self,url)
        return self    # 返回self,是方便链式调用

    def get_error_msg(self):
        elem = self.driver.find_elemet(*self.error_msg_locator)
        text = [el.text for el in elem]
        return text   # 返回所需文本信息

执行用例,进行调用,是如何操作的呢,如下:

#  用例编写调用(导入loginpages)

class TestLogin:

    def without_username_and_password(self,get_url):  # get_url是conftest中的
        driver = get_url
        login_page = LoginPages(driver)
        login_page.get_driver()   # 此get_url为访问浏览器界面
        actual = login_page.login('','').get_error_msg()  # 链式调用
        expect = ['账号不能为空','密码不能为空']
        assert actual == expect

四、url 配置优化

==  配置信息可通过yaml文件、py文件、excel(但不推荐)
py文件直接导入模块就好

==   yaml文件的话,需要读取yaml数据,并通过路径拼接获取数据(config中的path.py); 编写读取yaml文件的程序,是通用的
YAML文件写入:写入数据为:字段冒号后面要加一空格
如:host: "https://v4.ketangpai.com"
yaml文件读取编写如下:
# 读取yaml文件
def read_yaml(fpath):
    with open(fpath,encoding = 'utf-8')as f:
        data = yaml.safe_load(f)
        return data

五、数据驱动

数据驱动 ==> 思想,
参数化 ==> 实现数据驱动的手段,
ddt ==> data-driver-testing 数据驱动测试

===   数据驱动:
如有同一套代码,就只有数据不一样
就是当测试用例所有逻辑都一样,只有数据不同时,可把数据提取出来,
然后用数据去分别带入测试用例中
但是数据驱动不是万能的有些场景会不适用(如登录成功、登录失败,其断言验证的方式不一样,故无法进行ddt)

===   数据驱动的好处:
 - 实现了数据和代码分离,好维护
 - 简化代码,不需重复编写函数名
 
===     劣处:
 - 数据驱动不方便调试 ==》解决方式:可先注释无异常的数据
 - 如测试逻辑不通用,是不适合做数据驱动的

import pytest

data = [(1,1,2),(2,3,4)]

@pytest.mark.parametrize('info',data)
def test_add(info):
    # assert info[0] + info[1] == info[2]   # 方法一:通过索引的方式
    param1,param2,expecct = info    # 方式二,通过赋值的方式
    assert param1 + param2 == expecct

@pytest.mark.parametrize('param1,param2,expecct',data)   # 方法三:直接传入赋值变量
def test_demo(param1,param2,expecct):
    assert param1 + param2 == expecct

六、BasePage

BasePage作用:
对浏览器的操作进行封装,是每个项目通用的,可在不同项目中使用
我们封装的basepage的方法越多,后面框架会越容易使用,是一个长期优化的过程。
封装:非常明显的信号是:对一段代码进行复制、粘贴时,

BasePage本质:
是对selenium的二次开发,
如cypress、playwright,当觉得框架不好用时,都可进行二次开发。
from datetime import datetime
import os
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


class BasePage:

    host = ''

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

    def get(self,url):
        self.driver.get(url)
        if url.startswith('http'):
            self.driver.get(url)
        else:
            url = self.host + url
            self.driver.get(url)
        return self

    def click(self,locator,force = False):  # 点击操作
        elem = self.driver.find_element(*locator)
        # 方法一:
        # elem.click()
        # 方法二:如果元素不可点击时,可使用:
        if not force:
            self.driver.execute_script("arguments[0].click()",elem)
        else:
            self.driver.execute_script(f"arguments[0].click(r'{force:true})",elem)
        return self

    def fill(self,locator,text):  # 输入操作
        elem = self.driver.find_element(*locator)
        elem.send_keys(text)
        return self

    def get_attribute(self,locator,attr_name):  # 获取某个元素的name属性,可随需要修改
        elem = self.driver.find_element(*locator)
        return elem.get_attribute(attr_name)

    @property     # 添加property后,表属性,后面调用不用加(),即elem.name即可
    def name(self):   # 获取name属性
        return self.get_attribute('name')

    def double_click(self,locator):   # 上级操作
        elem = self.driver.find_element(*locator)
        action = ActionChains(self.driver)
        action.double_click(elem).perform()
        return self

    def drag_and_drop(self,start_locator,end_locator):  # 拖拽操作
        start_elem = self.driver.find_element(*start_locator)
        end_element = self.driver.find_element(*end_locator)
        action = ActionChains(self.driver)
        action.drag_and_drop(start_elem,end_element).perform()
        return self

    def move_to(self,locator):  # 鼠标悬停
        elem = self.driver.find_element(*locator)
        action = ActionChains(self.driver)
        action.move_to_element(elem).perform()
        return self

    def wait_visible(self,locator,timeout=10,poll=0.2):   # 等待元素可见
        wait = WebDriverWait(self.driver,timeout = timeout,poll_frequency=poll)
        el = wait.until(expected_conditions.visibility_of_all_elements_located(locator))  #本身为元组参数,故无需拆包
        return el

    def wait_presence(self,locator,timeout=10,poll=0.2):    # 等待元素出现
        wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll)
        el = wait.until(expected_conditions.presence_of_element_located(locator))  # 本身为元组参数,故无需拆包
        return el

    def wait_click(self,locator,timeout=10,poll=0.2):  # 等待元素可点击
        wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll)
        el = wait.until(expected_conditions.element_to_be_clickable(locator))  # 本身为元组参数,故无需拆包
        return el

    def switch_to_frame(self,referce,timeout=10,poll=0.2):  # iframe切换
        # referce可为:name、id、element对象、索引、locator
        if not referce:
            return self.driver.switch_to.default_content()  # 切回主界面
        wait = WebDriverWait(self.driver, timeout=timeout, poll_frequency=poll)
        el = wait.until(expected_conditions.frame_to_be_available_and_switch_to_it(referce))  # 本身为元组参数,故无需拆包
        return el

    def find_elements(self,locator):
        return self.driver.find_elements(*locator)

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

    def screenshot(self):
        current_time = datetime.now().strftime('%Y-%m-%d-%H-%M-%S')
        file_name = f'屏幕截图 - {current_time}。png'
        file_path = os.path.join(path.img_page, file_name)
        self.driver.get_screenshot_as_file(file_path)
        return self

if __name__ == '__main__':
    driver = webdriver.Chrome()
    d = BasePage(driver)
    d.get('http://www.baidu.com')
    d.fill(('id','kw'),'haha')
    d.click(('id','su'))

项目调用:

import random
import time

from selenium.webdriver.common.by import By
from common.Basepage import Basepage
from common.yaml_handler import info_data


class Create_classPage(Basepage):

    host = info_data['host']
    url = host + '/Main/index.html'

    # 操作的元素定位
    add_or_create_class_locator = ( By.XPATH,"//span[text()='+ 创建/加入课程']" )
    create_class_locator = (By.XPATH,"//a[text()='创建课程']")
    coures_name_input_locator = (By.XPATH,"//input[@placeholder='请输入课程名称']")
    class_name_input_locator = (By.XPATH,"//input[@placeholder='请输入班级名称(选填)']")
    creat_butto_locator = (By.XPATH,"//a[text()='创建']")
    cancel_locator = (By.XPATH,"//div[@class='pop-btns']//a[text()='取消']")
    close_locator = (By.XPATH,"//i[@class='close-newClass']")

    # 断言元素表达式
    actovar_locator = (By.XPATH,"//img[@title='xxx']")

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

    def loading(self):
        self.driver.get(self.url)
        return self

    def click_add_or_create_class(self,):
        self.click(self.add_or_create_class_locator)
        return self

    def click_create_class(self):
        self.click(self.create_class_locator)
        return self

    def fill_coures_name(self,text):
        self.send_keys(self.coures_name_input_locator,text)
        return text

    def fill_class_name(self,text):
        self.send_keys(self.class_name_input_locator,text)
        return text

    def click_create(self):
        self.click(self.creat_butto_locator)
        return self

    def click_cancel(self):
        self.click(self.cancel_locator)
        return self

    def click_close(self):
        self.click(self.close_locator)
        return self

    @staticmethod
    def gen_class_name():
        """随机生成课程名称, 字母,生成10长度的字母。
        aacdeyualp
        """
        name = ''
        for i in range(10):
            letter = random.choice('abcdefghijklmnopqrstuvwxyz')
            name += letter
        return name + str(int(time.time()))

总结

以上仅做记录、方便复习,还不太全面,之后再继续优化把。

日常鸡汤:最近都在坚持运动,看自己能不能稍微瘦那么一点点,也控制饮食,看能不能有所不同吧,关键还是要:坚持坚持坚持,同样,学习也是如此,fighting!!~~

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-07-31 16:56:13  更:2021-07-31 16:57:13 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 20:43:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码