用户端Web自动化测试-L2

目录:

  1. 高级定位-css
  2. 高级定位-xpath
  3. 显式等待高级使用
  4. 高级控件交互方法
  5. 网页 frame 与多窗口处理
  6. 文件上传,弹框处理
  7. 自动化关键数据记录
  8. 电子商务产品实战

1.高级定位-css

css 选择器概念

  • css 选择器有自己的语法规则和表达式
  • css 定位通常分为绝对定位和相对定位
  • 和Xpath一起常用于UI自动化测试中的元素定位

CSS 常用的选择器_常用选择器_阿瞒有我良计15的博客-CSDN博客

css 定位场景

  • 支持web产品
  • 支持app端的webview

 css 相对定位的优点

  • 可维护性更强
  • 语法更加简洁
  • 解决各种复杂的定位场景
# 绝对定位
$("#ember63 > td.main-link.clearfix.topic-list-data > span > span > a")
# 相对定位
$("#ember63 [title='新话题']")

css 定位的调试方法

  • 进入浏览器的console
  • 输入:
    • $("css表达式")
    • 或者$$("css表达式")

 css基础语法

类型表达式
标签标签名
.class属性值
ID#id属性值
属性[属性名='属性值']
//在console中的写法
// https://www.baidu.com/
//标签名
$('input')
//.类属性值
$('.s_ipt')
//#id属性值
$('#kw')
//[属性名='属性值']
$('[name="wd"]')

 css关系定位

类型格式
并集元素,元素
邻近兄弟(了解即可)元素+元素
兄弟(了解即可)元素1~元素2
父子元素>元素
后代元素 元素
//在console中的写法

//元素,元素
$('.bg,.s_ipt_wr,.new-pmd,.quickdelete-wrap')

//元素+元素,了解即可
$('.soutu-btn+input')

//元素1~元素2,了解即可
$('.soutu-btn~i')

//元素>元素
$('#s_kw_wrap>input')

//元素 元素
$('#form input')

 css 顺序关系

类型格式
父子关系+顺序元素 元素
父子关系+标签类型+顺序元素 元素
//:nth-child(n)
$('#form>input:nth-child(2)')
//:nth-of-type(n)
$('#form>input:nth-of-type(1)')

2.高级定位-xpath

xpath基本概念

  • XPath 是一门在 XML 文档中查找信息的语言
  • XPath 使用路径表达式在 XML 文档中进行导航
  • XPath 的应用非常广泛
  • XPath 可以应用在UI自动化测试

xpath 定位场景

  • web自动化测试
  • app自动化测试

 xpath 相对定位的优点

  • 可维护性更强
  • 语法更加简洁
  • 相比于css可以支持更多的方式

# 复制的绝对定位
$x('//*[@id="ember75"]/td[1]/span/a')
# 编写的相对行为
$x("//*[text()='技术分享 | SeleniumIDE用例录制']")

xpath 定位的调试方法

  • 浏览器-console
    • $x("xpath表达式")
  • 浏览器-elements
    • ctrl+f 输入xpath或者css

xpath 基础语法(包含关系)

表达式结果
/从该节点的子元素选取
//从该节点的子孙元素选取
*通配符
nodename选取此节点的所有子节点
..选取当前节点的父节点
@选取属性
# 整个页面
$x("/")
# 页面中的所有的子元素
$x("/*")
# 整个页面中的所有元素
$x("//*")
# 查找页面上面所有的div标签节点
$x("//div")
# 查找id属性为site-logo的节点
$x('//*[@id="site-logo"]')
# 查找节点的父节点
$x('//*[@id="site-logo"]/..')

xpath 顺序关系(索引)

  • xpath通过索引直接获取对应元素
# 获取此节点下的所有的li元素
$x("//*[@id='ember21']//li")
# 获取此节点下【所有的节点的】第一个li元素
$x("//*[@id='ember21']//li[1]")

xpath 高级用法

  • [last()]: 选取最后一个
  • [@属性名='属性值' and @属性名='属性值']: 与关系
  • [@属性名='属性值' or @属性名='属性值']: 或关系
  • [text()='文本信息']: 根据文本信息定位
  • [contains(text(),'文本信息')]: 根据文本信息包含定位
  • 注意:所有的表达式需要和[]结合
# 选取最后一个input标签
//input[last()]
# 选取属性name的值为passward并且属性pwd的值为123456的input标签
//input[@name='passward' and @pwd='123456']
# 选取属性name的值为passward或属性pwd的值为123456的input标签
//input[@name='passward' or @pwd='123456']
# 选取所有文本信息为'hello world'的元素
//*[text()='hello world']
# 选取所有文本信息包'hello'的元素
//*[contains(text(),'hello')]

3.显式等待高级使用

显式等待原理

  • 在代码中定义等待一定条件发生后再进一步执行代码
  • 在最长等待时间内循环执行结束条件的函数
  • WebDriverWait(driver 实例, 最长等待时间, 轮询时间).until(结束条件函数)

显式等待-expected_conditions

  • Selenium 显式等待官网说明
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait


def test_wait_until():
    driver = webdriver.Chrome()
    driver.get("https://vip.ceshiren.com/#/ui_study")
    WebDriverWait(driver, 10, 0.5).until(expected_conditions.element_to_be_clickable((By.CSS_SELECTOR, '#success_btn')))
    driver.find_element(By.CSS_SELECTOR, "#success_btn").click()

常见 expected_conditions

类型示例方法说明
elementelement_to_be_clickable()
visibility_of_element_located()
针对于元素,比如判断元素是否可以点击,或者元素是否可见
urlurl_contains()针对于 url
titletitle_is()针对于标题
frameframe_to_be_available_and_switch_to_it(locator)针对于 frame
alertalert_is_present()针对于弹窗

显式等待-封装等待条件

  • 官方的 excepted_conditions 不可能覆盖所有场景
  • 定制封装条件会更加灵活、可控

 显式等待-封装等待条件

import time
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium.webdriver.support import expected_conditions


class TestWebdriverWait:
    def setup(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(5)
        self.driver.get("https://vip.ceshiren.com/#/ui_study")

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

    def test_wait_until(self):
        WebDriverWait(self.driver, 10, 0.5).until(
            expected_conditions.element_to_be_clickable((By.CSS_SELECTOR, '#success_btn')))
        self.driver.find_element(By.CSS_SELECTOR, "#success_btn").click()

    def test_webdriver_wait(self):
        # 解决的问题:有的按钮点击一次没有反应,可能要点击多次,比如企业微信的添加成员
        # 解决的方案:一直点击按钮,直到下个页面出现,封装成显式等待的一个条件
        def muliti_click(button_element, until_ele):
            # 函数封装
            def inner(driver):
                # 封装点击方法
                driver.find_element(By.XPATH, button_element).click()
                return driver.find_element(By.XPATH, until_ele)

            return inner

        time.sleep(5)
        # 在限制时间内会一直点击按钮,直到展示弹框
        WebDriverWait(self.driver, 10).until(muliti_click("//*[text()='点击两次响应']", "//*[text()='该弹框点击两次后才会弹出']"))
        time.sleep(5)

4.高级控件交互方法

使用场景对应事件
复制粘贴键盘事件
拖动元素到某个位置鼠标事件
鼠标悬停鼠标事件
滚动到某个元素滚动事件
使用触控笔点击触控笔事件(了解即可)

官网:

https://www.selenium.dev/documentation/webdriver/actions_api

ActionChains解析

  • 实例化类ActionChains,参数为driver实例。
  • 中间可以有多个操作。
  • .perform()代表确定执行。
ActionChains(self.driver).操作.perform()

 键盘事件

  • 按下、释放键盘键位
  • 结合send_keys回车
  • Keyboard actions | Selenium

键盘事件-使用shift实现大写

  • ActionChains(self.driver): 实例化ActionChains类
  • key_down(Keys.SHIFT, ele): 按下shift键实现大写
  • send_keys("selenium"): 输入大写的selenium
  • perform(): 确认执行

代码示例:

import time

from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By


class TestKeyBoardDemo:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_key_down_up(self):
        self.driver.get("https://ceshiren.com/")
        self.driver.find_element(By.ID, "search-button").click()
        ele = self.driver.find_element(By.ID, "search-term")
        ActionChains(self.driver).key_down(Keys.SHIFT, ele).send_keys("selenium").perform()
        time.sleep(2)

键盘事件-输入后回车

  • 直接输入回车: 元素.send_keys(Keys.ENTER)
  • 使用ActionChains: key_down(Keys.ENTER)

 代码示例:

import time

from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By


class TestKeyBoardDemo:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_key_down_up(self):
        self.driver.get("https://ceshiren.com/")
        self.driver.find_element(By.ID, "search-button").click()
        ele = self.driver.find_element(By.ID, "search-term")
        ActionChains(self.driver).key_down(Keys.SHIFT, ele).send_keys("selenium").perform()
        time.sleep(2)

    def test_enter_by_send_keys(self):
        self.driver.get("https://www.sogou.com/")
        ele = self.driver.find_element(By.ID, "query")
        ele.send_keys("selenium")
        # 第一种方式
        # ele.send_keys(Keys.ENTER)
        # 第二种方式
        ActionChains(self.driver).key_down(Keys.ENTER).perform()
        time.sleep(3)

 键盘事件-复制粘贴

  • 多系统兼容
    • mac 的复制按钮为 COMMAND
    • windows 的复制按钮为 CONTROL
  • 左箭头:Keys.ARROW_LEFT
  • 按下COMMAND或者CONTROL: key_down(cmd_ctrl)
  • 按下剪切与粘贴按钮: send_keys("xvvvvv")
import sys
import time

from selenium import webdriver
from selenium.webdriver import ActionChains, Keys
from selenium.webdriver.common.by import By


class TestKeyBoardDemo:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_key_down_up(self):
        self.driver.get("https://ceshiren.com/")
        self.driver.find_element(By.ID, "search-button").click()
        ele = self.driver.find_element(By.ID, "search-term")
        ActionChains(self.driver).key_down(Keys.SHIFT, ele).send_keys("selenium").perform()
        time.sleep(2)

    def test_enter_by_send_keys(self):
        self.driver.get("https://www.sogou.com/")
        ele = self.driver.find_element(By.ID, "query")
        ele.send_keys("selenium")
        # 第一种方式
        # ele.send_keys(Keys.ENTER)
        # 第二种方式
        ActionChains(self.driver).key_down(Keys.ENTER).perform()
        time.sleep(3)

    def test_copy_and_paste(self):
        self.driver.get("https://ceshiren.com/")
        cmd_ctrl = Keys.COMMAND if sys.platform == 'darwin' else Keys.CONTROL
        self.driver.find_element(By.ID, "search-button").click()
        ele = self.driver.find_element(By.ID, "search-term")
        # 打开搜索,选择搜索框,输入selenium,剪切后复制,几个v就代表复制几次
        ActionChains(self.driver) \
            .key_down(Keys.SHIFT, ele) \
            .send_keys("Selenium!") \
            .send_keys(Keys.ARROW_LEFT) \
            .send_keys(Keys.ARROW_LEFT) \
            .send_keys(Keys.ARROW_LEFT) \
            .key_down(cmd_ctrl) \
            .send_keys("xvvvvv") \
            .key_up(cmd_ctrl) \
            .perform()
        time.sleep(10)

鼠标事件

  • 双击
  • 拖动元素
  • 指定位置(悬浮)
  • Mouse actions | Selenium

 鼠标事件-双击

  • double_click(元素对象): 双击元素

代码示例:

import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By


class TestMouseDemo:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_double_click(self):
        # 演练环境
        self.driver.get("https://vip.ceshiren.com/#/ui_study")
        ele = self.driver.find_element(By.ID, "primary_btn")
        ActionChains(self.driver).double_click(ele).perform()
        time.sleep(2)

 鼠标事件-拖动元素

  • drag_and_drop(起始元素对象, 结束元素对象): 拖动并放开元素

代码示例:

import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By


class TestMouseDemo:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_double_click(self):
        # 演练环境
        self.driver.get("https://vip.ceshiren.com/#/ui_study")
        ele = self.driver.find_element(By.ID, "primary_btn")
        ActionChains(self.driver).double_click(ele).perform()
        time.sleep(2)

    def test_drag_and_drop(self):
        self.driver.get("https://vip.ceshiren.com/#/ui_study/action_chains")
        item_left = self.driver.find_element(By.CSS_SELECTOR, '#item1')
        item_right = self.driver.find_element(By.CSS_SELECTOR, '#item3')
        ActionChains(self.driver).drag_and_drop(item_left, item_right).perform()
        time.sleep(5)

 鼠标事件-悬浮(下拉框)

  • move_to_element(元素对象): 移动到某个元素
import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By


class TestMouseDemo:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_double_click(self):
        # 演练环境
        self.driver.get("https://vip.ceshiren.com/#/ui_study")
        ele = self.driver.find_element(By.ID, "primary_btn")
        ActionChains(self.driver).double_click(ele).perform()
        time.sleep(2)

    def test_drag_and_drop(self):
        self.driver.get("https://vip.ceshiren.com/#/ui_study/action_chains")
        item_left = self.driver.find_element(By.CSS_SELECTOR, '#item1')
        item_right = self.driver.find_element(By.CSS_SELECTOR, '#item3')
        ActionChains(self.driver).drag_and_drop(item_left, item_right).perform()
        time.sleep(5)

    def test_hover(self):
        self.driver.get("https://vip.ceshiren.com/#/ui_study/action_chains2")
        time.sleep(2)
        title = self.driver.find_element(By.CSS_SELECTOR, '.title')
        ActionChains(self.driver).move_to_element(title).perform()
        options = self.driver.find_element(By.CSS_SELECTOR, '.options>div:nth-child(3)')
        ActionChains(self.driver).click(options).perform()
        time.sleep(5)

滚轮/滚动操作

  • 滚动到元素
  • 根据坐标滚动
  • 注意: selenium 版本需要在 4.2 之后
  • Scroll wheel actions | Selenium

 滚轮/滚动操作-滚动到元素

  • scroll_to_element(WebElement对象):滚动到某个元素

代码示例:

import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By


class TestScrollDemo:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_scoll_to_element(self):
        # 演练环境
        self.driver.get("https://ceshiren.com")
        # 4.2 之后才提供这个方法

        time.sleep(2)
        ele = self.driver.find_element(By.XPATH, "//*[text()='性能测试的目的']")

        ActionChains(self.driver).scroll_to_element(ele).perform()
        time.sleep(2)

 滚轮/滚动操作-根据坐标滚动

import time

from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By


class TestScrollDemo:

    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_scoll_to_element(self):
        # 演练环境
        self.driver.get("https://ceshiren.com")
        # 4.2 之后才提供这个方法

        time.sleep(2)
        ele = self.driver.find_element(By.XPATH, "//*[text()='性能测试的目的']")

        ActionChains(self.driver).scroll_to_element(ele).perform()
        time.sleep(2)

    def test_scroll_to_amount(self):
        # 演练环境
        self.driver.get("https://ceshiren.com/")
        # 4.2 之后才提供这个方法
        ActionChains(self.driver).scroll_by_amount(0, 10000).perform()
        time.sleep(2)

5.网页 frame 与多窗口处理

  • selenium⾥⾯如何处理多窗口场景
    • 多个窗⼜识别
    • 多个窗⼜之间切换
  • selenium⾥⾯如何处理frame
    • 多个frame识别
    • 多个frame之间切换

多窗口处理

  • 点击某些链接,会重新打开⼀个窗口,对于这种情况,想在新页⾯上操作,就得先切换窗口了。
  • 获取窗口的唯⼀标识⽤句柄表⽰,所以只需要切换句柄,就可以在多个页⾯灵活操作了。 

 多窗口处理流程

  1. 先获取到当前的窗⼜句柄(driver.current_window_handle)
  2. 再获取到所有的窗⼜句柄(driver.window_handles)
  3. 判断是否是想要操作的窗⼜,如果是,就可以对窗⼜进⾏操作,如果不是,跳转到另外⼀个窗⼜,对另⼀个窗⼜进⾏操作(driver.switch_to_window)

多窗⼜切换案例

  • 打开百度页⾯
  • 点击登录,
  • 弹框中点击‘⽴即注册’,输⼊⽤户名和帐号
  • 返回刚才的登录页,点击登录
  • 输⼊⽤户名和密码,点击登录 

base.py

from selenium import webdriver


class Base():
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

 test_window.py

import time

from selenium import webdriver
from selenium.webdriver.common.by import By

from web_automation_testing.test_keyboard_incident.base import Base


class TestWindow(Base):
    def test_window(self):
        self.driver.get("https://www.baidu.com")
        self.driver.find_element(By.LINK_TEXT, "登录").click()
        print(self.driver.current_window_handle)
        print(self.driver.window_handles)

        self.driver.find_element(By.XPATH,'//*[@id="TANGRAM__PSP_11__regLink"]').click()
        print(self.driver.current_window_handle)
        print(self.driver.window_handles)

        windows = self.driver.window_handles
        self.driver.switch_to.window(windows[-1])

        self.driver.find_element(By.CSS_SELECTOR,'#TANGRAM__PSP_4__userName').send_keys("username")
        self.driver.find_element(By.CSS_SELECTOR,'#TANGRAM__PSP_4__phone').send_keys("password")
        time.sleep(2)
        self.driver.switch_to.window(windows[0])
        self.driver.find_element(By.CSS_SELECTOR,'#TANGRAM__PSP_11__userName').send_keys("username")
        self.driver.find_element(By.CSS_SELECTOR,'#TANGRAM__PSP_11__password').send_keys("password")
        time.sleep(5)

frame介绍

  • 在web⾃动化中,如果⼀个元素定位不到,那么很⼤可能是在iframe中。
  • 什么是frame?
    • frame是html中的框架,在html中,所谓的框架就是可以在同⼀个浏览器中显⽰不⽌⼀个页⾯。
    • 基于html的框架,又分为垂直框架和⽔平框架(cols,rows)
  • Frame分类
    • frame标签包含frameset、frame、iframe三种,
    • frameset和普通的标签⼀样,不会影响正常的定位,可以使⽤index、id、name、webelement任意种⽅式定位frame。
    • ⽽frame与iframe对selenium定位⽽⾔是⼀样的。selenium有⼀组⽅法对frame进⾏操作

 多frame切换

  • frame存在两种:
    • ⼀种是嵌套的,⼀种是未嵌套的
  • 切换frame
    • driver.switch_to.frame()   #根据元素id或者index切换切换frame
    • driver.switch_to.default_content()  #切换到默认frame
    • driver.switch_to.parent_frame()   #切换到⽗级frame

frame未嵌套 

  • 处理未嵌套的iframe
    • driver.switch_to_frame(“frame的id”)
    • driver.switch_to_frame(“frame - index”)  frame⽆ID的时候依据索引来处理,索引从0开始driver.switch_to_frame(0)

Frame嵌套

  • 处理嵌套的iframe
  • 对于嵌套的先进⼊到iframe的⽗节点,再进到⼦节点,然后可以对⼦节点⾥⾯的对象进⾏处理和操作
  • driver.switch_to.frame(“⽗节点”)
  • driver.switch_to.frame(“⼦节点”) 

多frame切换案例

  • 多frame切换案例:
  • 打开包含frame的web页⾯菜鸟教程在线编辑器
  • 打印’请拖拽我’元素的⽂本
  • 打印’点击运⾏’元素的⽂本 

base.py 

from selenium import webdriver


class Base():
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

 test_frame.py

from selenium.webdriver.common.by import By

from web_automation_testing.test_keyboard_incident.base import Base


class TestFrame(Base):
    def test_frame(self):
        self.driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
        self.driver.switch_to.frame("iframeResult")
        print(self.driver.find_element(By.ID,'draggable').text)
        self.driver.switch_to.parent_frame()
        print(self.driver.find_element(By.ID, "submitBTN").text)

6.文件上传,弹框处理

  • ⽂件上传的⾃动化
  • 弹框处理机制

 ⽂件上传

  • input标签可以直接使⽤send_keys(⽂件地址)上传⽂件
  • ⽤法:
    • el = driver.find_element_by_id('上传按钮id')
    • el.send_keys(”⽂件路径+⽂件名")

⽂件上传实例

  • 测试案例:
  • 打开百度图⽚⽹址:https://image.baidu.com
  • 识别上传按钮
  • 点击上传按钮
  • 将本地的图⽚⽂件上传 

base.py 

from selenium import webdriver


class Base():
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

 test_file.py

import time

from selenium.webdriver.common.by import By

from web_automation_testing.test_keyboard_incident.base import Base


class TestFile(Base):
    def test_up_file(self):
        self.driver.get("https://image.baidu.com/")

        self.driver.find_element(By.CSS_SELECTOR,'#sttb > img.st_camera_off').click()
        self.driver.find_element(By.ID,'stfile').send_keys("E:\pictures\water.png")
        time.sleep(5)

chrome开启debug模式

有时候登录⽅式⽐较繁琐,需要动态⼿机密码,⼆维码登录之类的。⾃动话实现⽐较⿇烦。⼿⼯登录后,不想让selenium启动⼀个新浏览器。可以使⽤chrome的debug⽅式来执⾏测试。

  • 启动chrome的时候需要先退出所有chrome进程。使⽤ps aux|grep chrome|grep -v 'grep'查看是否有chrome进程存在。确保没有chrome进程被启动过。
  • 正常启动chrome的debug模式
    • #默认macOS系统
      • /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
    • # Windows下找到chrome.exe位置执⾏下⾯的命令
      • chrome.exe --remote-debugging-port=9222

启动后的提⽰信息,代表chrome运⾏正常,不要关闭⾃动打开的chrome窗口。

弹框处理机制 

  • 在页⾯操作中有时会遇到JavaScript所⽣成的alert、confirm以及prompt弹框,可以使⽤switch_to.alert()⽅法定位到。然后使⽤text/accept/dismiss/send_keys等⽅法进⾏操作。参考教你分辨alert、window、div模态框,以及操作
  • 操作alert常⽤的⽅法:
    • switch_to.alert():获取当前页⾯上的警告框。
    • text:返回alert/confirm/prompt中的⽂字信息。
    • accept():接受现有警告框。
    • dismiss():解散现有警告框。
    • send_keys(keysToSend):发送⽂本⾄警告框。keysToSend:将⽂本发送⾄警告框。

alert窗⼜处理案例

  • 测试案例:
    • 打开⽹页https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable
  • 操作窗⼜右侧页⾯,将元素1拖拽到元素2
  • 这时候会有⼀个alert弹框,点击弹框中的’确定
  • 然后再按’点击运⾏
  • 关闭⽹页 

base.py

from selenium import webdriver


class Base():
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

 test_alert.py

import time

from selenium.webdriver import ActionChains
from selenium.webdriver.common.by import By

from web_automation_testing.test_keyboard_incident.base import Base


class TestAlert(Base):
    def test_alert(self):
        self.driver.get("https://www.runoob.com/try/try.php?filename=jqueryui-api-droppable")
        self.driver.switch_to.frame("iframeResult")

        drag = self.driver.find_element(By.ID, "draggable")
        drop = self.driver.find_element(By.ID, "droppable")

        action = ActionChains(self.driver)
        action.drag_and_drop(drag, drop).perform()
        time.sleep(2)
        self.driver.switch_to.alert.accept()
        self.driver.switch_to.default_content()
        self.driver.find_element(By.ID, "submitBTN").click()
        time.sleep(3)

7.自动化关键数据记录

什么是关键数据?

  • 代码的执行日志
  • 代码执行的截图
  • page source(页面源代码)

记录关键数据的作用

内容作用
日志1. 记录代码的执行记录,方便复现场景
2. 可以作为bug依据
截图1. 断言失败或成功截图
2.异常截图达到丰富报告的作用
3. 可以作为bug依据
page source1. 协助排查报错时元素当时是否存在页面上

 行为日志记录

  • 日志配置
  • 脚本日志级别
    • debug记录步骤信息
    • info记录关键信息,比如断言等

代码示例:

from selenium import webdriver
from selenium.webdriver.common.by import By

from web_automation_testing.test_key_data.utils.log_util import logger


class TestDataRecord:
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_log_data_record(self):
        # 实例化self.driver
        search_content = "海贼王"
        # 打开百度首页
        self.driver.get("https://www.sogou.com/")
        logger.debug("打开搜狗首页")
        # 输入海贼王
        self.driver.find_element(By.CSS_SELECTOR, "#query").send_keys(search_content)
        logger.debug(f"搜索的内容为{search_content}")
        # 点击搜索
        self.driver.find_element(By.CSS_SELECTOR, "#stb").click()
        # 搜索结果
        search_res = self.driver.find_element(By.ID, "sogou_vr_70060202_title_0")
        logger.info(f"搜索结果为{search_res.text}")
        assert search_res.text == search_content

 步骤截图记录

  • save_screenshot(截图路径+名称)
  • 记录关键页面
    • 断言页面
    • 重要的业务场景页面
    • 容易出错的页面
from selenium import webdriver
from selenium.webdriver.common.by import By

from web_automation_testing.test_key_data.utils.log_util import logger


class TestDataRecord:
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_log_data_record(self):
        # 实例化self.driver
        search_content = "海贼王"
        # 打开百度首页
        self.driver.get("https://www.sogou.com/")
        logger.debug("打开搜狗首页")
        # 输入海贼王
        self.driver.find_element(By.CSS_SELECTOR, "#query").send_keys(search_content)
        logger.debug(f"搜索的内容为{search_content}")
        # 点击搜索
        self.driver.find_element(By.CSS_SELECTOR, "#stb").click()
        # 搜索结果
        search_res = self.driver.find_element(By.ID, "sogou_vr_70060202_title_0")
        logger.info(f"搜索结果为{search_res.text}")
        assert search_res.text == search_content

    def test_screenshot_record(self):
        # 实例化self.driver
        search_content = "海贼王"
        # 打开百度首页
        self.driver.get("https://www.sogou.com/")
        logger.debug("打开搜狗首页")
        # 输入海贼王
        self.driver.find_element(By.CSS_SELECTOR, "#query").send_keys(search_content)
        logger.debug(f"搜索的内容为{search_content}")
        # 点击搜索
        self.driver.find_element(By.CSS_SELECTOR, "#stb").click()
        # 搜索结果
        search_res = self.driver.find_element(By.ID, "sogou_vr_70060202_title_0")
        logger.info(f"搜索结果为{search_res.text}")
        self.driver.save_screenshot("./datas/screenshot/search_res.png")
        assert search_res.text == search_content

 page_source记录

  • 使用page_source属性获取页面源码
  • 在调试过程中,如果有找不到元素的错误可以保存当时的page_source调试代码
from selenium import webdriver
from selenium.webdriver.common.by import By

from web_automation_testing.test_key_data.utils.log_util import logger


class TestDataRecord:
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)

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

    def test_log_data_record(self):
        # 实例化self.driver
        search_content = "海贼王"
        # 打开百度首页
        self.driver.get("https://www.sogou.com/")
        logger.debug("打开搜狗首页")
        # 输入海贼王
        self.driver.find_element(By.CSS_SELECTOR, "#query").send_keys(search_content)
        logger.debug(f"搜索的内容为{search_content}")
        # 点击搜索
        self.driver.find_element(By.CSS_SELECTOR, "#stb").click()
        # 搜索结果
        search_res = self.driver.find_element(By.ID, "sogou_vr_70060202_title_0")
        logger.info(f"搜索结果为{search_res.text}")
        assert search_res.text == search_content

    def test_screenshot_record(self):
        # 实例化self.driver
        search_content = "海贼王"
        # 打开百度首页
        self.driver.get("https://www.sogou.com/")
        logger.debug("打开搜狗首页")
        # 输入海贼王
        self.driver.find_element(By.CSS_SELECTOR, "#query").send_keys(search_content)
        logger.debug(f"搜索的内容为{search_content}")
        # 点击搜索
        self.driver.find_element(By.CSS_SELECTOR, "#stb").click()
        # 搜索结果
        search_res = self.driver.find_element(By.ID, "sogou_vr_70060202_title_0")
        logger.info(f"搜索结果为{search_res.text}")
        self.driver.save_screenshot("./datas/screenshot/search_res.png")
        assert search_res.text == search_content

    def test_page_source_record(self):
        # 实例化self.driver
        search_content = "海贼王"
        # 打开百度首页
        self.driver.get("https://www.sogou.com/")

        logger.debug("打开搜狗首页")

        with open("./datas/page_source/record.html", "w", encoding="utf8") as f:
            f.write(self.driver.page_source)

8.电子商务产品实战

产品分析:

  • 产品:litemall管理后台
  • 功能:商品类目
  • 使用账户
    • 用户名: manage
    • 密码: manage123

litemall

测试用例分析

用例标题前提条件用例步骤预期结果实际结果
添加商品类目1. 登录并进入用户管理后台
2. 登录账号有商场管理的权限
1. 点击增加
2. 输入类目名称
3. 点击确定
1. 跳转商品类目列表
2. 新增在最后一行,新增成功
删除商品类目1. 进入用户管理后台
2. 商品列表里面有已存在的商品(新增)
1. 点击删除按钮1. 是否有删除成功提示
2. 被删除商品不在商品类目列表展示

编写脚本思路:

PlantUML diagram

前置后置

  • 在setup_class打开浏览器
  • teardown_class关闭浏览器进程
  • 添加隐式等待配置

 test_litemall.py

import time

import allure
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

from web_automation_testing.test_litemall.utils.log_util import logger


class TestLitemall:
    # 前置动作
    def setup_class(self):
        self.driver = webdriver.Chrome()
        self.driver.implicitly_wait(3)
        # 登录
        self.driver.get("http://litemall.hogwarts.ceshiren.com/")
        # 问题,输入框内有默认值,此时send——keys不回清空只会追加
        # 解决方案: 在输入信息之前,先对输入框完成清空
        # 输入用户名密码
        self.driver.find_element(By.NAME, "username").clear()
        self.driver.find_element(By.NAME, "username").send_keys("manage")
        self.driver.find_element(By.NAME, "password").clear()
        self.driver.find_element(By.NAME, "password").send_keys("manage123")
        # 点击登录按钮
        self.driver.find_element(By.CSS_SELECTOR, ".el-button--primary").click()
        # 窗口最大化
        self.driver.maximize_window()

    # 后置动作
    def teardown_class(self):
        self.driver.quit()

    def get_screen(self):
        timestamp = int(time.time())
        # 注意:!! 一定要提前创建好images 路径
        image_path = f"./datas/screenshot/image_{timestamp}.PNG"
        # 截图
        self.driver.save_screenshot(image_path)
        # 讲截图放到报告的数据中
        allure.attach.file(image_path, name="picture",attachment_type=allure.attachment_type.PNG)

    # 新增功能
    def test_add_type(self):
        # 点击商场管理/商品类目,进入商品类目页面
        # 进入商品类目页面
        self.driver.find_element(By.XPATH, "//*[text()='商场管理']").click()
        self.driver.find_element(By.XPATH, "//*[text()='商品类目']").click()
        # 添加商品类目操作
        self.driver.find_element(By.XPATH, "//*[text()='添加']").click()
        self.driver.find_element(By.CSS_SELECTOR, ".el-input__inner").send_keys("新增商品测试")

        # ===============使用显式等待优化
        # ele = WebDriverWait(self.driver,10).until(
        #     expected_conditions.element_to_be_clickable(
        #         (By.CSS_SELECTOR, ".dialog-footer .el-button--primary")))
        # ele.click()
        
        # ==============显示等待优化方案2: 自定义显式等待条件
        def click_exception(by, element, max_attempts=5):
            def _inner(driver):
                # 多次点击按钮
                actul_attempts = 0  # 实际点击次数
                while actul_attempts < max_attempts:
                    # 进行点击操作
                    actul_attempts += 1  # 每次循环,实际点击次数加1
                    try:
                        # 如果点击过程报错,则直接执行 except 逻辑,并切继续循环
                        # 没有报错,则直接return 循环结束
                        driver.find_element(by, element).click()
                        return True
                    except Exception:
                        logger.debug("点击的时候出现了一次异常")
                # 当实际点击次数大于最大点击次数时,结束循环并抛出异常
                raise Exception("超出了最大点击次数")

            # return _inner() 错误写法
            return _inner

        WebDriverWait(self.driver, 10).until(click_exception(By.CSS_SELECTOR, ".dialog-footer .el-button--primary"))

        # ===========================使用显式等待优化

        # self.driver.find_element(By.CSS_SELECTOR, ".dialog-footer .el-button--primary").click()
        # finds 如果没找到会返回空列表, find 如果没找到则会直接报错
        # 如果没找到,程序也不应该报错
        res = self.driver.find_elements(By.XPATH, "//*[text()='新增商品测试']")
        self.get_screen()
        # 数据的清理一定到放在断言操作之后完成,要不然可能会影响断言结果
        self.driver.find_element(By.XPATH, "//*[text()='新增商品测试']/../..//*[text()='删除']").click()
        logger.info(f"断言获取到的实际结果为{res}")
        # 断言产品新增后是否成功找到,如果找到,证明新增成功,如果没找到则新增失败
        # 判断查找的结果是否为空列表,如果为空列表证明没找到,反之代表元素找到,用例执行成功
        assert res != []

    # 删除功能
    def test_delete_type(self):
        # ================ 造数据步骤
        # 点击商场管理/商品类目,进入商品类目页面
        # 进入商品类目页面
        self.driver.find_element(By.XPATH, "//*[text()='商场管理']").click()
        self.driver.find_element(By.XPATH, "//*[text()='商品类目']").click()
        # 添加商品类目操作
        self.driver.find_element(By.XPATH, "//*[text()='添加']").click()
        self.driver.find_element(By.CSS_SELECTOR, ".el-input__inner").send_keys("删除商品测试")
        ele = WebDriverWait(self.driver, 10).until(
            expected_conditions.element_to_be_clickable(
                (By.CSS_SELECTOR, ".dialog-footer .el-button--primary")))
        ele.click()
        # ============完成删除步骤
        self.driver.find_element(By.XPATH, "//*[text()='删除商品测试']/../..//*[text()='删除']").click()
        # 断言: 删除之后获取这个 删除商品测试的 这个商品类目是否还能获取到,如果获取到,证明没有删除成功,反之删除成功
        WebDriverWait(self.driver, 10).until_not(
            expected_conditions.visibility_of_any_elements_located((By.XPATH, "//*[text()='删除商品测试']")))
        # 问题: 因为代码执行速度过快,元素还未消失就捕获了。
        # 解决: 确认该元素不存在后,再捕获
        res = self.driver.find_elements(By.XPATH, "//*[text()='删除商品测试']")
        logger.info(f"断言获取到的实际结果为{res}")
        assert res == []

目录结构:

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java IO流(四)Netty理论[模型|核心组件]

概述 Netty是由JBOSS提供的一个Java开源框架,可从Github获取独立项目Netty是一个异步的、基于事件驱动的网络应用框架,用于快速开发可维护、高性能的网络服务器和客户端(摘录官网)Netty所谓的异步是针对用户使用Channel进行IO操作,会立即返回ChannelFuture。但IO操作的任务是提…

开源容灾备份软件,开源cdp备份软件

数据的安全性和完整性面临着硬件问题、黑客攻击、人为错误等各种威胁。在这种环境下&#xff0c;开源容灾备份软件应运而生&#xff0c;通过提供自动数据备份和恢复&#xff0c;有效地保证了公司的数据安全。 一、开源容灾备份软件的定义和作用 开源容灾备份软件是一种基于开源…

SqlServer2019—解决SQL Server 无法连接127.0.0.1的问题

1、打开SQL Server 2019配置管理器 2、SQL Servere 网络配置(启用 Named Pipes 和 TCP/IP) 3、修改TCP/IP协议(右键选择属性—IP地址)&#xff0c;具体如下图所示&#xff1a; 4、重启SQL Server服务

漏洞挖掘和漏洞利用技术:讨论漏洞发现、利用和修复,深入研究不同类型漏洞的技术细节

章节一&#xff1a;引言 在当今数字化时代&#xff0c;计算机技术的迅猛发展为我们的生活带来了无数便利&#xff0c;然而也伴随着各种安全威胁。恶意黑客利用漏洞进行攻击已成为一种常见现象。本文将深入探讨漏洞挖掘和漏洞利用技术&#xff0c;以及如何修复这些漏洞&#xf…

智慧充电桩物联网方案架构

智慧充电桩物联网采用“云-管-边-端”的边缘计算物联网架构&#xff0c;融合5G、AI、Wi-Fi 6等技术&#xff0c;实现充电基础设施由数字化向智能化演进。智慧充电桩物联网方案架构设计&#xff0c;如下图所示&#xff1a; 云端&#xff1a; 物联网平台具备广泛协议的南向接入…

基于QCC_BES 平台的LMS自适应滤波算法实现

+我V hezkz17进数字音频系统研究开发交流答疑群(课题组) LMS算法是最小均方(Least Mean Square)算法的缩写。它是一种自适应滤波算法,常用于信号处理、系统辨识和自适应滤波等领域。 LMS算法的目标是通过对输入信号和期望输出信号之间的误差进行最小化,来调整滤波器的权重…

matlab使用教程(22)—非线性优化函数的设置

1.设置优化选项 可以使用由 optimset 函数创建的 options 结构体来指定优化参数。然后&#xff0c;可以将 options 作为输入传递给优化函数&#xff0c;例如&#xff0c;通过使用以下语法调用 fminbnd x fminbnd(fun,x1,x2,options) 或使用以下语法调用 fminsearch x f…

8月21-22日上课内容 第一章 MySQL数据库初始

本章结构 数据库的基本概念 概述&#xff08;总览&#xff09; 结构&#xff1a; 数据 表 数据库 数据库管理系统 数据库系统原理 数据 (Data) 描述事物的符号记录 包括数字&#xff0c;文字、图形、图像、声音、档案记录等以“记录”形式按统一的格式进行存储表 将不同…

验证码识别DLL ,滑块识别SDK,OCR图片转文字,机器视觉找物品

验证码识别DLL ,滑块识别SDK 你们用过哪些OCR提取文字&#xff0c;识图DLL&#xff0c;比如Opencv,Labview机器视觉找物品之类&#xff1f;

Selenium的使用:WEB功能测试

Selenium是ThrougthWorks公司一个强大的开源WEB功能测试工具系列&#xff0c;本系统包括多款软件 Selenium语言简单&#xff0c;用(Command,target,value)三种元素组成一个行为&#xff0c;并且有协助录制脚本工具&#xff0c;但Selenese有一些严格的限制&#xff1a; …

FFmpeg解码32k大分辨率出现如下错误:Picture size 32768x32768 is invalid

最近找到一张32k的jpeg图片&#xff0c;尝试用ffmpeg来进行解码&#xff0c;命令如下&#xff1a; ffmpeg -i enflame_32768-32768-420.jpg 32.yuv结果出现Picture size 32768x32768 is invalid的错误&#xff1a; 找到报错的代码文件imgutils.c&#xff0c;以及函数&#x…

Shiro学习总结

第一章 入门概述 1.概念 shiro是一个Java安全框架&#xff0c;可以完成&#xff1a;认证、授权、加密、会话管理、与web集成、缓存… 2.优势 ● 易于使用&#xff0c;构建简单 ● 功能全面 ● 灵活&#xff0c;可以在任何应用程序环境中工作&#xff0c;并且不需要依赖它们…

RabbitMQ笔记-RabbitMQ基本术语

RabbitMQ基本术语 相关概念; 生产者&#xff08;Producer&#xff09;&#xff1a;投递消息。消息&#xff1a;消息体&#xff08;payload&#xff09;标签&#xff08;label&#xff09;&#xff1b;生产者把消息交给rabbitmq&#xff0c;rabbitmq会根据标签把消息发给感兴趣…

【自动驾驶】TI SK-TDA4VM 开发板上电调试,AI Demo运行

1. 设备清单 TDA4VM Edge AI 入门套件【略】USB 摄像头(任何符合 V4L2 标准的 1MP/2MP 摄像头,例如:罗技 C270/C920/C922)全高清 eDP/HDMI 显示屏最低 16GB 高性能 SD 卡连接到互联网的 100Base-T 以太网电缆【略】UART电缆外部电源或电源附件要求: 标称输出电压:5-20VDC…

第1章:计算机网络体系结构

文章目录 1.1 计算机网络 概述1.概念2.组成3.功能4.分类5.性能指标1.2 计算机网络 体系结构&参考模型1.分层结构2.协议、接口、服务3.ISO/OSI模型4.TCP/IP模型1.1 计算机网络 概述 1.概念 2.组成 1.组成部分&

多线程基础篇(包教包会)

文章目录 一、第一个多线程程序1.Jconsole观察线程2.线程休眠-sleep 二、创建线程三、Thread类及常见方法1. Thread 的常见构造方法2. Thread 的几个常见属性3. 启动线程 - start4. 中断线程5. 等待一个线程 四、线程状态五、线程安全问题(synchronized)&#xff08;重点&#…

深入理解回调函数qsort:从入门到模拟实现

&#x1f341;博客主页&#xff1a;江池俊的博客 &#x1f4ab;收录专栏&#xff1a;C语言进阶之路 &#x1f4a1;代码仓库&#xff1a;江池俊的代码仓库 &#x1f3aa;我的社区&#xff1a;GeekHub &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐ 文章目录 前…

七夕学算法

目录 P1031 [NOIP2002 提高组] 均分纸牌 原题链接 : 题面 : 思路 : 代码 : P1036 [NOIP2002 普及组] 选数 原题链接 : 题面 : 思路 : 代码 : P1060 [NOIP2006 普及组] 开心的金明 原题链接 : 题面 : 思路 : 01背包例题 : 代码 : P1100 高低位交换 原题…

HarmonyOS开发第一步,熟知开发工具DevEco Studio

俗话说的好&#xff0c;工欲善其事&#xff0c;必先利其器&#xff0c;走进HarmonyOS第一步&#xff0c;开发工具必须先行&#xff0c;当然了&#xff0c;关于开发工具的使用&#xff0c;官网和其他的博客也有很多的讲解&#xff0c;但是并没有按照常用的功能进行概述&#xff…

诚迈科技子公司智达诚远与Unity中国达成合作,打造智能座舱新时代

2023 年 8 月 23 日&#xff0c;全球领先的实时 3D 引擎 Unity 在华合资公司 Unity 中国举办发布会&#xff0c;正式对外发布 Unity 引擎中国版——团结引擎&#xff0c;并带来专为次世代汽车智能座舱打造的团结引擎车机版。发布会上&#xff0c;诚迈科技副总裁、诚迈科技子公司…