Web的UI自动化基础知识

news2024/12/28 18:02:56

目录

  • 1 Web自动化入门基础
    • 1.1 自动化知识以及工具
    • 1.2 主流web自动化测试工具
    • 1.3 入门案例
  • 2 使用工具的API
    • 2.1 元素定位
      • 2.1.1 id选择器
      • 2.1.2 name
      • 2.1.3 class_name选择器
      • 2.1.4 tag_name选择器
      • 2.1.5 link_text选择器
      • 2.1.6 partial_link_text选择器
      • 2.1.7 xpath选择器
      • 2.1.8 CSS选择器
      • 2.1.9 Xpath和CSS区别
      • 2.1.10 元素定位分类
      • 2.1.11 元素定位的另一种写法
    • 2.2 元素操作
    • 2.3 浏览器操作
    • 2.4 获取元素信息
    • 2.5 鼠标操作
      • 2.5.1 常用方法
      • 2.5.2 执行的方法
      • 2.5.3 鼠标右击
      • 2.5.4 鼠标双击
      • 2.5.5 鼠标悬停
      • 2.5.6 鼠标拖动
    • 2.6 键盘操作
      • 2.6.1 常用操作
      • 2.6.2 键盘操作
      • 2.6.3 元素等待
      • 2.6.4 隐式等待
      • 2.6.5 显式等待
      • 2.6.6 隐式和显式区别
      • 2.6.7 下拉框
      • 2.6.8 弹出框分类
    • 2.7 滚动条实现方法
    • 2.8 frame切换
      • 2.8.1 多窗口切换
      • 2.8.2 窗口截图
    • 2.9 验证码处理
    • 2.10 cookie
  • 3 Pytest框架
    • 3.1 总体介绍
    • 3.2 断言方法
    • 3.3 setup和teardown
    • 3.4 配置文件
    • 3.5 测试报告插件
    • 3. 6 数据参数化
      • 3.6.1 单一参数
      • 3.6.2 多个参数
      • 3.6.3 推荐用法
  • 4 PO模式
    • 4.1 递进学习路线
    • 4.2 无模式
      • 4.2.1 案例说明
      • 4.2.2 选择测试用例
    • 4.3 V1版本
    • 4.4 V2版本
      • 4.4.1 方法封装
    • 4.5 V3版本
    • 4.6 PO模式
      • 4.6.1 概念
      • 4.6.2 PO模式分层
      • 4.6.3 PO模式优点
    • 4.7 V4版本
    • 4.8 v5版本
    • 4.9 v6版本
      • 4.9.1 示例代码
  • 5 数据驱动
    • 5.1 JSON基本介绍
    • 5.2 字典与JSON转换
    • 5.3 JSON文件读写
  • 6 项目实战
    • 6.1 项目结构
    • 6.2 base包
    • 6.3 data包
    • 6.4 page包
    • 6.5 scripts包
    • 6.6 utils包
  • 7 日志收集
    • 7.1 日志收集
    • 7.2 日志高级用法
    • 7.3 四大组件
      • 7.3.1 Logger类
      • 7.3.2 Handler类
      • 7.3.3 Formatter类
  • 8 面试题

1 Web自动化入门基础

1.1 自动化知识以及工具

自动化概念 :由机器设备代替人工自动完成指定目标的过程

优点:

  1. 减少人工劳动力
  2. 提高工作效率
  3. 产品规格统一标准
  4. 规模化
  5. 安全

自动化测试概念 :由程序代替人工去执行测试的过程

应用场景

  1. 解决回归测试
    • 已实现的功能需要回归
    • 已解决的bug需要回归
  2. 解决压力测试:例如使用Jmeter做接口自动化
  3. 解决兼容性测试:在不同浏览器上做兼容性测试
  4. 解决操作重复性问题

1.2 主流web自动化测试工具

  1. QTP :收费且支持web/桌面自动化测试
  2. selenium:开源web自动化测试工具(功能测试)【跨平台、支持多种浏览器、支持多种语言、稳定】
  3. robot framework :基于python的可扩展的关键字驱动的自动化测试框架

1.3 入门案例

# 导包
import time

from selenium import webdriver

# 创建浏览器驱动
driver = webdriver.Chrome()
# 打开百度首页
driver.get("http://www.taobao.com")
# 暂停3秒
time.sleep(10)
# 关闭浏览器
driver.quit()

在这里插入图片描述

2 使用工具的API

2.1 元素定位

八种定位方式

2.1.1 id选择器

案例:

打开https://parabank.parasoft.com/parabank/admin.htm网站首页,完成以下操作

  • 最大化页面

  • 使用ID定位,输入用户名:admin

  • 使用ID定位,输入密码:12345

  • 等待5s,关闭浏览器

import time

from selenium import webdriver

# 获取驱动对象
driver = webdriver.Chrome()
# 获取地址
driver.get("https://parabank.parasoft.com/parabank/admin.htm")
# 页面最大化
driver.maximize_window()
# 根据id查找元素
driver.find_element_by_id('username').send_key("admin")
driver.find_element_by_id('password').send_key("123456")
# 等待五秒
time.sleep(5)
# 关闭浏览器
driver.quit()

2.1.2 name

案例:

打开https://parabank.parasoft.com/parabank/admin.htm 网站首页,完成以下操作

  • 最大化页面

  • 使用name定位,输入用户名:admin

  • 使用name定位,输入密码:12345

  • 等待5s,关闭浏览器

import time

from selenium import webdriver

# 获取驱动对象
driver = webdriver.Chrome()
# 获取地址
driver.get("https://parabank.parasoft.com/parabank/admin.htm")
# 页面最大化
driver.maximize_window()
# 根据id查找元素
driver.find_element_by_name('username').send_key("admin")
driver.find_element_by_name('password').send_key("123456")
# 等待五秒
time.sleep(5)
# 关闭浏览器
driver.quit()

2.1.3 class_name选择器

案例:

打开https://parabank.parasoft.com/parabank/admin.htm网站首页,完成以下操作

  • 最大化页面

  • 使用class_name定位,输入用户名:admin

  • 使用class_name定位,输入密码:12345

  • 等待5s,关闭浏览器

import time

from selenium import webdriver

# 获取驱动对象
driver = webdriver.Chrome()
# 获取地址
driver.get("https://parabank.parasoft.com/parabank/admin.htm")
# 页面最大化
driver.maximize_window()
# 根据id查找元素
driver.find_element_by_class_name('username').send_key("admin")
driver.find_element_by_class_name('password').send_key("123456")
# 等待五秒
time.sleep(5)
# 关闭浏览器
driver.quit()

2.1.4 tag_name选择器

# 1.导包
import time
from selenium import webdriver

# 2.创建浏览器驱动对象
driver = webdriver.Chrome()

# 3.业务操作
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element_by_tag_name("input").send_keys("xxxxxx")

# 4.暂停5秒
time.sleep(5)

# 5.关闭驱动对象
driver.quit()

2.1.5 link_text选择器

# 1.导包
import time
from selenium import webdriver

# 2.创建浏览器驱动对象
driver = webdriver.Chrome()

# 3.业务操作
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element_by_link_text("访问 新浪 网站").click()

# 4.暂停5秒
time.sleep(5)

# 5.关闭驱动对象
driver.quit()

2.1.6 partial_link_text选择器

# 1.导包
import time
from selenium import webdriver

# 2.创建浏览器驱动对象
driver = webdriver.Chrome()

# 3.业务操作
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# driver.find_element_by_partial_link_text("访问 新浪 网站").click()    # 通过全部文本定位超链接
driver.find_element_by_partial_link_text("访问").click()    # 通过局部文本定位超链接

# 4.暂停5秒
time.sleep(5)

# 5.关闭驱动对象
driver.quit()

定位一组元素

# 1.导包
import time
from selenium import webdriver

# 2.创建浏览器驱动对象
driver = webdriver.Chrome()

# 3.业务操作
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
elements = driver.find_elements_by_tag_name("input")
elements[1].send_keys("123456")

# 4.暂停5秒
time.sleep(5)

# 5.关闭驱动对象
driver.quit()

2.1.7 xpath选择器

四种定位方式

  1. 路径
  2. 元素属性
  3. 属性与逻辑结合
  4. 层级与属性结合

方法

element = driver。find_element_by_xpath(xpath)

路径

  • 绝对路径:
    1. 从外层元素到指定元素之间所有经过元素层级的路径
    2. 绝对路径以/html根节点开始,使用/来分割元素层级,如:/html/body/div/fieldset/p[1]/input
    3. 绝对路径对页面要求严格,不建议使用
  • 相对路径
    1. 匹配任意层级的元素,不限制元素的位置
    2. 相对路径//开始
    3. 格式://input 或者 //*
import time
from selenium import webdriver

driver = webdriver.Chrome()

driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 定位用户名输入框, 输入 admin
driver.find_element_by_xpath("/html/body/div/fieldset/form/p[1]/input").send_keys("admin")
# 暂停3s
time.sleep(3)
# 定位密码输入框, 输入 123
driver.find_element_by_xpath("//*[@id='passwordA']").send_keys("123")

time.sleep(5)
driver.close()

使用谷歌浏览器获取 XPath 表达式的过程:

  1. 元素上右键 -> 检查

  2. 在F12对应的文档中的对应元素上 右键 -> Copy -> Copy XPath 或者 Copy full XPath

使用函数

不使用函数时:
//*[@id='xxx']

使用函数后
//*[text()='xxx']   文本内容是 xxx 的元素
//*[contains(@attribute, 'xxx')] 属性中含有 xxx 值的元素
//*[starts-with(@attribute, 'xxx')] 属性以xxx开头的元素

案例

import time
from selenium import webdriver

driver = webdriver.Chrome()

driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 利用元素属性通过XPath 定位用户名输入框, 并输入 admin
# driver.find_element_by_xpath("//*[@name='userA']").send_keys("admin")
# driver.find_element_by_xpath("//*[@id='userA']").send_keys("admin")
# driver.find_element_by_xpath("//*[@placeholder='请输入用户名']").send_keys("admin")
driver.find_element_by_xpath("//*[@type='text']").send_keys("admin")

time.sleep(5)
driver.close()

2.1.8 CSS选择器

常用的定位方式

  • id选择器
  • class选择器
  • 元素选择器
  • 属性选择器
  • 层级选择器

方法

element = driver.find_element_by_css_selector(css表达式)

id

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面, 使用CSS定位方式中的id选择器, 定位用户名输入框, 并输入 admin
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element_by_css_selector("#userA").send_keys("admin")

time.sleep(5)
driver.close()

class

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面, 使用CSS定位方式中的class选择器, 定位电话号码输入框, 并输入 13100000000
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element_by_css_selector(".telA").send_keys("13100000000")

time.sleep(5)
driver.close()

元素选择器

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面, 使用CSS定位方式中的元素选择器, 定位注册按钮, 并点击
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element_by_css_selector("button").click()

time.sleep(5)
driver.close()

属性选择器

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面, 使用CSS定位方式中的属性选择器, 定位密码输入框, 并输入 123456
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
driver.find_element_by_css_selector("[type='password']").send_keys("123456")

time.sleep(5)
driver.close()

层级选择器

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面, 使用CSS定位方式中的层级选择器, 定位用户名输入框, 并输入 admin
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# driver.find_element_by_css_selector("p[id='pa']>input").send_keys("admin")
driver.find_element_by_css_selector("div[class='zc'] input").send_keys("admin")

time.sleep(5)
driver.close()

CSS扩展

  1. input[type^=‘p’] type属性以p字母开头的元素
  2. input[type$=‘d’] type属性以d字母结束的元素
  3. input[type*=‘w’] type属性包含w字母的元素
import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面, 使用CSS定位扩展的方式, 定位用户名输入框, 并输入 admin
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# driver.find_element_by_css_selector("input[type^='t']").send_keys("admin")
# driver.find_element_by_css_selector("input[name^='u']").send_keys("admin")
# driver.find_element_by_css_selector("input[type$='t']").send_keys("admin")
driver.find_element_by_css_selector("input[type*='ex']").send_keys("admin")

time.sleep(5)
driver.close()

2.1.9 Xpath和CSS区别

XPath和CSS对比
	通过标签名定位
		XPath
			//input
		CSS
			input
	通过id属性定位
		XPath
			//*[@id='userA']
		CSS
			#userA
	通过class属性定位
		XPath
			//*[@class='telA']
		CSS
			.telA
	通过其他属性定位
		XPath
			//*[starts-with(@type,'x')]
				以x字母开头的type值的元素
			//*[contains(@type, 'x')]
				包含x字母的type值的元素
			//*[text()='x']
				文本内容为 x 的元素
		CSS
			[type^='x']
				以x字母开头的type值的元素
			[type*='x']
				包含x字母的type值的元素
			[type$='x']
				以x字母结尾的type值的元素

2.1.10 元素定位分类

  1. id, name, class_name: 元素属性定位
  2. tag_name: 元素标签名定位
  3. link_text, partial_link_text: 通过文本定位超链接
  4. XPath: 通过路径定位元素
  5. CSS: 使用CSS选择器定位

2.1.11 元素定位的另一种写法

方法

方法: driver.find_element(方式, 值)
备注:

  1. 需要2个参数, 第1个参数为定位的类型(由By提供), 第2个参数传入具体的值
  2. 如果要使用By, 需要导包

示例

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

driver = webdriver.Chrome()

driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")

# 八中定位方法都适用"另一种方法"
# driver.find_element(By.ID, "userA").send_keys("admin")
driver.find_element(By.XPATH, "//*[@placeholder='请输入电子邮箱']").send_keys("123456@qq.com")

time.sleep(5)
driver.close()

2.2 元素操作

方法

click() 单击元素
send_keys() 模拟输入
clear() 清除文本

案例

需求:打开注册A页面,完成以下操作

  1. 通过脚本执行输入用户名:admin;密码:123456;电话号码:18611111111;电子邮件:123@qq.com

  2. 间隔3秒,修改电话号码为:18600000000

  3. 间隔3秒,点击‘注册’按钮

  4. 间隔3秒,关闭浏览器

    注意:元素定位方法不限

代码

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

driver = webdriver.Chrome()

# 打开注册A页面,完成以下操作
# 1.通过脚本执行输入用户名:admin;密码:123456;电话号码:18611111111;电子邮件:123@qq.com
# 2.间隔3秒,修改电话号码为:18600000000
# 3.间隔3秒,点击‘注册’按钮
# 4.间隔3秒,关闭浏览器
# ps: 元素定位方法不限
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 1
driver.find_element_by_id("userA").send_keys("admin")
driver.find_element_by_id("passwordA").send_keys("123456")
driver.find_element_by_id("telA").send_keys("18611111111")
driver.find_element_by_name("emailA").send_keys("123@qq.com")
# 2
time.sleep(3)
driver.find_element_by_id("telA").clear()
driver.find_element_by_id("telA").send_keys("18600000000")
# 3
time.sleep(3)
driver.find_element_by_css_selector("body > div > fieldset > form > p:nth-child(5) > button").click()
# 4
time.sleep(3)
driver.close()

2.3 浏览器操作

import time
from selenium import webdriver

driver = webdriver.Chrome()

driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")

## maximize_window() 浏览器窗口最大化
driver.maximize_window()

## set_window_size() 设置窗口大小(单位:像素点)   set_window_position()  设置窗口的位置
driver.set_window_size(300, 300)
driver.set_window_position(300, 300)

## back() 后退 forward() 前进 refresh() 刷新
driver.back()
driver.forward()
time.sleep(3)
driver.refresh()

## title 获取页面标题     current_url  获取当前页面url
print("页面标题:", driver.title)
print("当前页面地址:", driver.current_url)

## driver.close()   关闭当前浏览器窗口  ==> 执行结果, 留下了新浪网站, 关闭了注册A页面
time.sleep(3)
driver.find_element_by_link_text("访问 新浪 网站").click()
time.sleep(3)
driver.close()

### 序号 30~48 的脚本应该使用 driver.quit() 关闭浏览器驱动 而不是 driver.close()
## driver.quit()    关闭浏览器驱动对象(关闭浏览器)    ==> 执行结果, 关闭所有窗口, 关闭浏览器驱动
time.sleep(3)
driver.find_element_by_link_text("访问 新浪 网站").click()
time.sleep(3)
driver.quit()

2.4 获取元素信息

应用场景

用于校验, 判断定位的元素是否准确

常用方法

size 返回元素大小
text 获取元素文本
get_attribute("xxx") 获取属性值, 参数是元素的属性名
is_displayed() 判断元素是否可见
is_enabled() 判断元素是否可用
is_selected() 判断元素是否选中, 用来检查复选框或单选按钮

案例

import time
from selenium import webdriver

driver = webdriver.Chrome()

driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")

# 需求: 打开A页面, 完成以下操作:
# 1.获取用户名输入框的大小
print(driver.find_element_by_id("userA").size)
# 2.获取页面上第一个超链接的文本内容
print(driver.find_element_by_tag_name("a").text)
# 3.获取页面上第一个超链接的地址
print(driver.find_element_by_tag_name("a").get_attribute("href"))
# 4.判断页面中的span标签是否可见
print(driver.find_element_by_tag_name("span").is_displayed())
# 5.判断页面中的取消按钮是否可用
print(driver.find_element_by_id("cancelA").is_enabled())
# 6.判断页面中的'旅游'对应的复选框是否为选中状态
print(driver.find_element_by_id("lyA").is_selected())

time.sleep(3)
driver.quit()

2.5 鼠标操作

什么是鼠标操作

单击, 右击, 双击, 悬停, 拖拽等

为什么要用到鼠标操作

现在web产品中存在丰富的鼠标交互方式, 作为一个web自动化测试框架, 需要应对这些鼠标操作的场景

2.5.1 常用方法

说明: 在Selenium中将鼠标操作的方法封装在 ActionChains 类中

实例化对象: action = ActionChains(driver)

方法:

  1. context_click(element) 右击
  2. double_click(element) 双击
  3. move_to_element(element) 悬停
  4. drag_and_drop(source, target) 拖拽
  5. perform() 执行

2.5.2 执行的方法

说明: 在 ActionChains 类中所有提供的鼠标事件方法, 在调用的时候, 所有行为都存储在 ActionChains 对象中, 而 perform() 方法就是真正去执行所有的鼠标事件

强调: 必须调用 perform() 方法才能执行鼠标事件

2.5.3 鼠标右击

说明: 对于点击鼠标右键, 如果弹出的是浏览器的默认菜单, Selenium并没有提供操作菜单的方法
如果是自定义的右键菜单, 则可以通过元素定位来操作菜单中的选项

需求: 打开A页面, 在用户名文本框上点击鼠标右键

import time

from selenium import webdriver
from selenium.webdriver import ActionChains

driver = webdriver.Chrome()

# 需求: 打开A页面, 在用户名文本框上点击鼠标右键
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")

# 定位用户名输入框
element = driver.find_element_by_id("userA")
# 执行右键点击操作
action = ActionChains(driver)
action.context_click(element).perform()

time.sleep(3)
driver.quit()

2.5.4 鼠标双击

说明: 模拟鼠标双击左键的操作

需求: 打开A页面, 输入用户名 admin, 暂停3s, 双击鼠标左键(选中admin)

import time
from selenium import webdriver
from selenium.webdriver import ActionChains

driver = webdriver.Chrome()

# 需求: 打开A页面, 输入用户名 admin, 暂停3s, 双击鼠标左键(选中admin)
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
element = driver.find_element_by_id("userA")
element.send_keys("admin")
time.sleep(3)
action = ActionChains(driver)
action.double_click(element).perform()

time.sleep(3)
driver.quit()

2.5.5 鼠标悬停

说明: 模拟鼠标悬停在指定元素上

需求: 打开A页面, 模拟鼠标悬停在 注册 按钮上

import time
from selenium import webdriver
from selenium.webdriver import ActionChains

driver = webdriver.Chrome()

# 需求: 打开A页面, 模拟鼠标悬停在 注册 按钮上
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
element = driver.find_element_by_tag_name("button")
action = ActionChains(driver)
action.move_to_element(element).perform()

time.sleep(3)
driver.quit()

2.5.6 鼠标拖动

说明: 模拟鼠标拖动动作, 选定拖动源元素释放到目标元素

  1. 源元素 source = driver.find_element_by_xxx("xxx")
  2. 目标元素 target = driver.find_element_by_xxx("xxx")
  3. 调用方法 action.drag_and_drop(source, target).perform()

需求: 打开 drag.html 页面, 把红色方框拖动到蓝色方框上

import time
from selenium import webdriver
from selenium.webdriver import ActionChains

driver = webdriver.Chrome()

# 需求: 打开 drag.html 页面, 把红色方框拖动到蓝色方框上
driver.get("file:///C:/Users/57769/Desktop/pagetest/drag.html")
red = driver.find_element_by_id("div1")
blue = driver.find_element_by_id("div2")
ActionChains(driver).drag_and_drop(red, blue).perform()

time.sleep(3)
driver.quit()

2.6 键盘操作

说明:

  1. 模拟键盘上的一些按键或者组合键的输入, 如: 复制/粘贴
  2. Selenium中把键盘的按键都封装在 Keys 类中

2.6.1 常用操作

导包

  1. send_keys(Keys.BACK_SPACE) 删除键(Backspace)
  2. send_keys(Keys.SPACE) 空格键(Space)
  3. send_keys(Keys.TAB) 制表键(Tab)
  4. send_keys(Keys.ESCAPE) 回退键(ESC)
  5. send_keys(Keys.ENTER) 回车键(Enter)
  6. send_keys(Keys.CONTROL, 'a') 全选(Ctrl + A)
  7. send_keys(Keys.CONTROL, 'c') 复制(Ctrl + C)

提示: 以上方法很多, 不会逐一讲解, 因为调用方法都一样

2.6.2 键盘操作

需求

打开 A 页面, 完成以下操作

  1. 输入用户名 admin1, 暂停2s, 删除1
  2. 全选用户名 admin 暂停2s
  3. 复制用户名 admin 暂停2s
  4. 粘贴到电话输入框

代码

import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys

driver = webdriver.Chrome()

driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 1. 输入用户名 admin1, 暂停2s, 删除1
element = driver.find_element_by_id("userA")
element.send_keys("admin1")
time.sleep(2)
element.send_keys(Keys.BACK_SPACE)
# 2. 全选用户名 admin 暂停2s
element.send_keys(Keys.CONTROL, "a")
time.sleep(2)
# 3. 复制用户名 admin 暂停2s
element.send_keys(Keys.CONTROL, "c")
time.sleep(2)
# 4. 粘贴到电话输入框
driver.find_element_by_id("telA").send_keys(Keys.CONTROL, "v")

time.sleep(5)
driver.quit()

2.6.3 元素等待

概念

定位页面元素, 如果未找到, 在指定时间内一直等待的过程

分类

  • 隐式等待
  • 显式等待

由于一些原因, 我们想找的元素并没有立刻出来, 此时直接定位会报错, 场景如下:

  1. 网络速度慢
  2. 服务器计算慢
  3. 硬件配置差

思考: 是否定位每个元素时, 都需要元素等待?

2.6.4 隐式等待

方法

隐式等待为全局设置 (只需要设置1次,会作用于所有元素)

参数:

timeout: 超时的时长, 单位: 秒

driver.implicitly_wait(timeout)

案例

import time
from selenium import webdriver

driver = webdriver.Chrome()

driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")

# 需求: 打开A页面, 使用隐式等待定位 "延时加载的输入框", 并输入 admin
driver.implicitly_wait(10)
driver.find_element_by_css_selector("input[placeholder='延时加载的输入框']").send_keys("admin")

time.sleep(3)
driver.quit()


# 不使用元素等待时, 如果找不到元素会报 NoSuchElementException 异常
# 使用隐式等待时, 如果找不到元素会报 NoSuchElementException 异常

注意点

单个元素定位超时会报 NoSuchElementException

2.6.5 显式等待

说明: 在Selenium中把显式等待的相关方法封装在 WebDriverWait 类中

方法 :

显式等待, 为定位不同的元素的超时时间设置不同的值

  1. 导包

  2. WebDriverWait(driver, timeout, poll_frequency=0.5)

    1. driver: 浏览器驱动对象
    2. timeout: 超时时长, 单位: 秒
    3. poll_frequency: 检测的间隔时间, 默认为0.5s
  3. 调用 until(method)

    1. method: 函数名称, 该函数用来实现元素定位
    2. 一般使用匿名来实现: lambda x: x.find_element_by_xxx("xxx")

    如:element = WebDriverWait(driver,10,1).until(lambda x: x.find_element_by_xxx("xxx"))

案例

import time
from selenium import webdriver
from selenium.webdriver.support.wait import WebDriverWait

driver = webdriver.Chrome()

driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")

# 需求: 打开A页面, 使用显式等待定位 "延时加载的输入框", 并输入 admin
wait = WebDriverWait(driver, 10, 1)
element = wait.until(lambda x: x.find_element_by_css_selector("input[placeholder='延时加载的输入框']"))
element.send_keys("admin")

time.sleep(3)
driver.quit()

# 单个元素定位超时会报错 TimeoutException

注意点

单个元素定位超时会报错 TimeoutException

2.6.6 隐式和显式区别

  1. 作用域: 隐式等待为全局有效, 显式等待为单个元素有效
  2. 使用方法: 隐式等待直接通过驱动对象调用, 而显式等待方法封装在 WebDriverWait 类中
  3. 达到最大超时时长后抛出异常不同: 隐式等待为 NoSuchElementException, 显式等待为 TimeoutException

2.6.7 下拉框

案例

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面,完成以下下拉框操作
# 1. 暂停2s, 选择广州
# 2. 暂停2s, 选择上海
# 3. 暂停2s, 选择北京
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 1
time.sleep(2)
driver.find_element_by_xpath("//*[@id='selectA']/option[3]").click()
# 2
time.sleep(2)
driver.find_element_by_xpath("//*[@id='selectA']/option[2]").click()
# 3
time.sleep(2)
driver.find_element_by_xpath("//*[@id='selectA']/option[1]").click()

time.sleep(3)
driver.quit()

案例

说明: Select类是Selenium为操作select标签封装的

实例化对象:
select = Select(element)
element: <select>标签对应的元素, 通过元素定位方式获取
例如: driver.find_element_by_id("selectA")

操作方法:

  1. select_by_index(index) 根据option索引来定位, 从0开始
  2. select_by_value(value) 根据option属性 value值来定位
  3. select_by_visible_text(text) 根据option显示文本内容来定位

步骤分析

  1. 导包
  2. 实例化Select类 select = Select(driver.find_element_by_id("selectA"))
  3. 调用方法

案例

import time
from selenium import webdriver
from selenium.webdriver.support.select import Select

driver = webdriver.Chrome()

# 需求: 打开A页面,完成以下下拉框操作
# 1. 暂停2s, 选择广州
# 2. 暂停2s, 选择上海
# 3. 暂停2s, 选择北京
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
select = Select(driver.find_element_by_id("selectA"))
# 1
time.sleep(2)
select.select_by_index(2)
# 2
time.sleep(2)
select.select_by_value("sh")
# 3
time.sleep(2)
select.select_by_visible_text("北京")

time.sleep(3)
driver.quit()

2.6.8 弹出框分类

  1. alert 警告框
  2. confirm 确认框
  3. prompt 提示框

弹出框的错误示范

错误代码

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面,完成以下弹出框操作
# 1.点击 alert 按钮
# 2.暂停2s, 输入用户名 admin
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 1
driver.find_element_by_id("alerta").click()
# 2
time.sleep(2)
driver.find_element_by_id("userA").send_keys("admin")

time.sleep(3)
driver.quit()

# 思考
# 1.什么问题导致的?
# driver的焦点在弹出框页面, 并不在A页面, 无法为你输入admin(找不到用户名输入框)
# 2.如何处理弹出框?

弹出框方法

说明: Selenium中对弹出框的处理, 有专用的方法, 且处理的方法都一样(alert/confirm/prompt)

1.获取弹出框对象
alert = driver.switch_to.alert
2.调用
alert.text 返回alert/confirm/prompt文字信息
alert.accept() 接受对话框选项(确认)
alert.dismiss() 取消对话框选项(取消)

案例

正确代码

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面,完成以下弹出框操作
# 1.点击 alert 按钮
# 2.暂停2s, 输入用户名 admin
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# 1
driver.find_element_by_id("alerta").click()
time.sleep(2)
alert = driver.switch_to.alert
print(alert.text)
time.sleep(2)
alert.accept()
# 2
time.sleep(2)
driver.find_element_by_id("userA").send_keys("admin")

time.sleep(3)
driver.quit()

2.7 滚动条实现方法

方法

说明: Selenium中没有提供滚动条的操作方法, 但是它提供了执行 JS 的方法, 所有我们可以通过 JS脚本来操作滚动条

  1. 设置 JS 脚本控制滚动条
    js = "window.scrollTO(0,1000)"
    (0:左边距, 1000:上边距 单位:像素(px))
  2. Selenium 调用执行 JS 脚本的方法
    driver.execute_script(js)

案例

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开A页面
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
# js1 滚动到最底部
js1 = "window.scrollTo(0, 10000)"
# js2 滚动到最顶部
js2 = "window.scrollTo(0, 0)"
# 执行第一个脚本
time.sleep(2)
driver.execute_script(js1)
# 执行第二个脚本
time.sleep(2)
driver.execute_script(js2)

time.sleep(3)
driver.quit()

2.8 frame切换

概念

frame : html页面中的一种框架, 主要作用是在当前页面指定区域显示另一个页面元素

形式一:
<frameset cols="25%,75%">
<frame src="a.html">
<frame src="b.html">
</frameset>
形式二:
<iframe name="iframe_a" src="demo.html" width="200" height="200"></iframe>

方法

说明: 在Selenium中封装了如何切换frame框架的方法

步骤:
1.driver.switch_to.frame(frame_reference) 切换到指定frame
frame_reference: 可以传frame框架的id,name,定位的frame元素
2.driver.switch_to.default_content() 恢复默认页面
必须回到默认页面才能进一步操作

解决方案

  1. 在主页面输入用户名 admin
  2. 切换到A页面, 再输入用户名 adminA
  3. 恢复默认页面
  4. 切换到B页面, 再输入用户名 adminB

正确代码

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开"注册实例"页面
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8C%E5%AE%9E%E4%BE%8B.html")
# 1.填写主页面的用户名 admin
time.sleep(2)
driver.find_element_by_id("userA").send_keys("admin")
# 2.填写注册页面A中的用户名 adminA
time.sleep(2)
# driver.switch_to.frame("idframe1")  # 从主页面, 切换到了A页面, 通过 id
# driver.switch_to.frame("myframe1")  # 从主页面, 切换到了A页面, 通过 name
driver.switch_to.frame(driver.find_element_by_id("idframe1"))  # 从主页面, 切换到了A页面, 通过 定位到的元素
driver.find_element_by_id("userA").send_keys("adminA")
# 3.回到主页面
time.sleep(1)
driver.switch_to.default_content()
# 4.填写注册页面B中的用户名 adminB
time.sleep(1)
driver.switch_to.frame("idframe2")  # 从主页面, 切换到B页面
driver.find_element_by_id("userA").send_keys("adminB")

time.sleep(3)
driver.quit()

2.8.1 多窗口切换

概念

什么是窗口? 窗口类似于浏览器中的标签页, 每个窗口就对应了一个标签页

为什么要切换窗口? 在html页面中, 当点击按钮或超链接时, 有的会在新窗口打开页面

如果点击按钮或超链接在当前窗口打开新页面, 就不需要切换窗口

需求

打开A页面

  1. 在新窗口打开新浪页面
  2. 在新浪的搜索框输入"新浪搜索"
  3. 在A页面输入用户名 admin

方法

说明: 在Selenium中封装了获取当前窗口句柄,获取所有窗口句柄和切换到指定句柄窗口的方法
句柄: 英文handle, 窗口的唯一识别码
方法:

1. `driver.current_window_handle`		获取当前窗口句柄
2. `driver.window_handles`				获取所有窗口句柄
3. `driver.switch_to.window(handle)`	切换到指定句柄的窗口

对于需求的解决方案:

  1. 打开A页面, 获取当前窗口句柄(拿到的是A页面的句柄)
  2. 在A页面点击"访问 新浪 网站" 这个超链接, 获取所有窗口句柄
  3. 根据句柄, 切换到新浪窗口, 对输入框输入 “新浪搜索”
  4. 切换回原本窗口(A页面), 输入用户名 admin

注意: 新浪页面需要访问网络, 可能加载慢, 可能需要用到元素等待

代码

import time
from selenium import webdriver

driver = webdriver.Chrome()
# 隐式等待10秒, 以防新浪窗口加载慢, 定位不到输入框
driver.implicitly_wait(10)

# 1. 打开A页面, 获取当前窗口句柄(拿到的是A页面的句柄)
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")
print("当前A页面窗口句柄:", driver.current_window_handle)
# 2. 在A页面点击"访问 新浪 网站" 这个超链接, 获取所有窗口句柄
driver.find_element_by_id("fw").click()
handles = driver.window_handles
print("所有窗口句柄:", handles)
# 3. 根据句柄, 切换到新浪窗口, 对输入框输入 "新浪搜索"
driver.switch_to.window(handles[1])
time.sleep(1)
driver.find_element_by_class_name("inp-txt").clear()
time.sleep(1)
driver.find_element_by_class_name("inp-txt").send_keys("新浪搜索")
time.sleep(2)
# 4. 切换回原本窗口(A页面), 输入用户名 admin
driver.switch_to.window(handles[0])
driver.find_element_by_id("userA").send_keys("admin")

time.sleep(3)
driver.quit()

2.8.2 窗口截图

概念

什么是窗口截图?

把当前操作的页面, 截图保存到指定的位置

为什么要窗口截图?

有时候打印的错误信息不十分准确, 需要窗口截图辅助定位错误

方法

说明: 在Selenium中提供了截图方法, 我们只需要调用即可

方法:
driver.get_screenshot_as_file(imgpath)
imgpath: 图片保存路径 + 图片名

案例

import time
from selenium import webdriver

driver = webdriver.Chrome()

# 需求: 打开 A 页面, 完成以下操作
# 1.输入用户名 admin
# 2.截图保存
driver.get("file:///C:/Users/57769/Desktop/pagetest/%E6%B3%A8%E5%86%8CA.html")

# 1
driver.find_element_by_id("userA").send_keys("admin")
# 2
time.sleep(1)
# 每次都是用固定文件名, 会股改上一次生成的图片文件
# driver.get_screenshot_as_file("./png/123.png")  # 需要提前创建 png 目录

# 使用时间去格式化文件名, 可以使每次截图保存的文件名都不同, 不会覆盖之前保存的文件, 更有效
imgpath = "./png/test_{}.png".format(time.strftime("%Y%m%d%H%M%S"))
driver.get_screenshot_as_file(imgpath)

time.sleep(3)
driver.quit()

2.9 验证码处理

概念

什么是验证码?

一种随机生成的信息 (数字, 字母, 汉字, 图片, 算术题…) 等为了防止恶意的请求行为, 增加应用的安全性

为什么要学习验证码?

在web应用中, 大部分系统在用户登录注册的时候都需要输入验证码, 而我们自动化脚本也要面临处理验证码的问题

常用方法

说明: Selenium中并没有对验证码处理的方法, 在这里我们介绍几种常用的处理方式

方法:
1.去掉验证码
(测试环境下采用)
2.设置万能验证码
(生产和测试环境下采用)
3.验证码识别技术
(通过 python-tesseract 来识别图片类型的验证码: 识别率很难达到100%)
4.记录 cookie
(通过记录 cookie 进行跳过登录)

注意

1 和 2, 都是开发人员来完成
3 验证码识别技术成功率不高, 不太合适
4 记录cookie 比较实用, 推荐

2.10 cookie

概念

  1. cookie是由web服务器生成的, 并且保存在用户浏览器上的小文本文件, 它可以包含用户信息
  2. cookie数据格式: 键值对 (python中的字典)
  3. cookie产生: 客户端请求服务器, 如果服务器需要记录该用户状态, 就向客户端浏览器颁发一个cookie数据
  4. cookie使用: 当浏览器再次请求该网站时, 浏览器把请求的数据和cookie数据一同提交给服务器, 服务器检查该cookie, 以此来辨认用户

应用场景

  1. 实现会话跟踪, 记录用户登录状态
  2. 实现记住密码和自动登录的功能
  3. 用户未登录状态下, 记录购物车中的商品

方法

说明: Selenium中对cookie操作提供相应的方法

方法:
1.driver.get_cookies() 获取本网站所有本地cookies
2.driver.get_cookie(name) 获取指定cookie
name: 为cookie中键值对数据的 键名
3.driver.add_cookie(cookie_dict) 添加cookie
cookie_dict: 一个字典对象, 必选的内容包括: “name” 和 “value”

案例需求

使用cookie 实现跳过百度登录
1.手动登录百度, 获取cookie
2.请求百度, 并且带上cookie

步骤分析

BDUSS是登录百度后的唯一身份凭证, 拿到BDUSS就等于拿到了账号的控制权,通行贴吧,知道,文库…主要产品

  1. 登录百度, 抓取BDUSS
  2. 添加 BDUSS 的键值对
  3. 调用刷新的方法

代码

import time
from selenium import webdriver

driver = webdriver.Chrome()
driver.maximize_window()

# 需求: 使用cookie 实现跳过百度登录
# 1.手动登录百度, 获取cookie
# 2.请求百度, 并且带上cookie

# 没有cookie的时候
driver.get("http://www.baidu.com")
# 添加cookie操作
driver.add_cookie({"name": "BDUSS", "value": "VZMUEl0WFJQYkxNSXk0c0VMUk5ZNGYteWVYNG01aVJtZXFCV056alk5M3V3SUZlSVFBQUFBJCQAAAAAAAAAAAEAAAC2KUFmTFhKX0pheQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAO4zWl7uM1peQ"})
time.sleep(3)
# 刷新, 再次请求百度首页, 验证是否带上身份信息
driver.refresh()

time.sleep(3)
driver.quit()

3 Pytest框架

3.1 总体介绍

什么是断言

让程序代替人工去判断测试程序的执行结果是否符合预期的过程

为什么学习断言

自动化脚本在执行的时候一般都是无人值守的状态, 我们不知道执行结果是否符合预期, 所以我们需要让程序代替人工去检测程序的执行结果是否符合预期, 这就需要断言

3.2 断言方法

assert xx 判断 xx 为真
assert not xx 判断 xx 不为真
assert a in b 判断 b 包含 a
assert a == b 判断 a 等于 b
assert a != b 判断 a 不等于 b

代码案例

def add(x, y):
    return x + y

class TestPlus:

    # 判断 1+1 的结果等于 2
    def test_a(self):
        assert 2 == add(1, 1)

    # 调换表达式两个值的位置, 判断 1+1 的结果等于 2
    def test_b(self):
        assert add(1, 1) == 2

    # 判断 1+2 的结果不等于4
    def test_c(self):
        assert 4 != add(1, 2)

    # 误判: 1+2 等于 4 了
    def test_d(self):
        assert 4 == add(1, 2)

3.3 setup和teardown

应用场景

pytest 在运行自动化脚本的前后会执行两个特殊的方法, 分别是"前置"和"后置"方法
在脚本执行前会执行"前置"方法,在脚本执行后会执行"后置"方法

概念和方法

1.初始化(前置处理方法):
def setup(self)
2.销毁(后置处理方法):
def teardown(self)
3.运行于测试方法的始末, 即:运行一次测试方法就会运行一次 setup 和 teardown

案例

import time


def add(x, y):
    return x + y

class TestPlus:

    # 获取并打印开始时间, 每个测试函数执行前都打印一次
    def setup(self):
        print("start-time:", time.time())

    # 获取并打印结束时间, 每个测试函数执行后都打印一次
    def teardown(self):
        print("end-time:", time.time())

    def test_a(self):
        assert 2 == add(1, 1)

    def test_b(self):
        assert add(1, 1) == 2

3.4 配置文件

应用场景

使用配置文件, 可以通过配置项来选择执行哪些目录下的哪些测试模块

用法

步骤:

  1. 新建 scripts 模块, 测试脚本放到模块中
  2. 新建 pytest.ini 文件, 名称为 pytest.ini, 第一行为 [pytest], 并且补全配置项
  3. 命令行运行 pytest 即可

示例

pytest.ini

[pytest]
addopts = -s
testpaths = ./scripts
python_files = test_*.py
python_classes = Test*
python_functions = test_*

3.5 测试报告插件

应用场景

需要测试报告来体现自动化脚本测试是否通过

安装

pip install pytest-html==1.21.1

使用

在配置文件中的命令行参数中, 增加 --html=用户路径/report.html

生成报告

步骤:

  1. 命令行输入 pytest 运行脚本
  2. 在项目目录下会有一个 report文件夹, 里面有个 report.html 就是测试报告

3. 6 数据参数化

应用场景

需要测试多组值得时候, 使用数据参数化可以使代码更简洁, 可读性更好

方法

数据参数化, 装饰器需要放在要传多组值的函数上

@pytest.mark.parametrize(argnames, argvalues)

参数:

argnames: 参数名
argvalues: 参数对应值, 类型必须是可迭代类型, 一般使用 list

3.6.1 单一参数

代码

import pytest


class TestDemo:

# 需求: 不使用数据参数化, 分别打印用户名 "zhangsan" 和 "lisi"
    def test_a(self):
        print("zhangsan")

    def test_b(self):
        print("lisi")

# 需求: 使用数据参数化 (单一参数), 修改上面的代码
    @pytest.mark.parametrize("name", ["zhangsan", "lisi"])
    def test_c(self, name):
        print(name)

3.6.2 多个参数

代码

import pytest


class TestDemo:

# 需求: 使用数据参数化 (多个参数), 分别打印2组账号和密码: zhangsan / 111111 和 lisi / 222222
    @pytest.mark.parametrize(("username", "password"), [("zhangsan", "111111"), ("lisi", "222222")])
    def test_c(self, username, password):
        print(username + "-----" + password)

# 使用元组可以传多个值  ("zhangsan", "111111"),   列表行不行?

3.6.3 推荐用法

代码

import pytest


class TestDemo:

# 需求: 使用数据参数化 (推荐用法), 分别打印2组账号和密码: zhangsan / 111111 和 lisi / 222222
#     @pytest.mark.parametrize(("username", "password"), [("zhangsan", "111111"), ("lisi", "222222")])
#     def test_c(self, username, password):
#         print(username + "-----" + password)

    @pytest.mark.parametrize("dict", [{"username": "zhangsan", "password": "111111"}, {"username": "lisi", "password": "222222"}])
    def test_d(self, dict):
        print(dict)
        print(dict["username"])
        print(dict["password"])
#("zhangsan", "111111", "13000000000", "1", "1", "30", "......")
#("lisi", "222222", "13100000000", ??????)
# 推荐的用法是用字典表示参数值
#  {"username": "zhangsan", "password": "111111"}

4 PO模式

4.1 递进学习路线

  • v1: 不使用任何设计模式和单元测试框架
  • v2: 使用 pytest 管理用例
  • v3: 使用方法封装的思想, 对代码进行优化
  • v4: 采用PO模式的分层思想对代码进行拆分, 分离page
  • v5: 对PO分层后的代码继续优化, 分离page中的元素和操作
  • v6: PO模式深入封装, 把共同操作提取封装

4.2 无模式

4.2.1 案例说明

对 TPshop 项目的登录模块进行自动化测试

登录模块包含了很多测试用例, 如: 账号不存在, 密码错误, 验证码错误, 登录成功等等

为了节省时间, 我们只选取几个有代表性的用例来演示: 账号不存在, 密码错误

4.2.2 选择测试用例

  • 账号不存在

    1. 点击首页的"登录"链接, 进入登录页面
    2. 输入一个不存在的用户名
    3. 输入密码
    4. 输入验证码
    5. 点击登录按钮
    6. 获取错误提示信息
  • 密码错误

    1. 点击首页的"登录"链接, 进入登录页面

    2. 输入用户名

    3. 输入一个错误的密码

    4. 输入验证码

    5. 点击登录按钮

    6. 获取错误提示信息

4.3 V1版本

  • 不使用任何设计模式和单元测试框架
  • 每个文件对应编写一个测试用例, 完全的面向过程的编程方式

示例代码

  1. 登录功能, 账号不存在
# 账号不存在
import time
from selenium import webdriver

# 实例化浏览器驱动
driver = webdriver.Chrome()
driver.maximize_window()
driver.implicitly_wait(10)
driver.get("http://localhost/")

# 1. 点击首页的"登录"链接, 进入登录页面
driver.find_element_by_class_name("red").click()
# 2. 输入一个不存在的用户名
driver.find_element_by_id("username").send_keys("18800000000")
# 3. 输入密码
driver.find_element_by_id("password").send_keys("123456")
# 4. 输入验证码
driver.find_element_by_id("verify_code").send_keys("8888")
# 5. 点击登录按钮
driver.find_element_by_name("sbtbutton").click()
# 6. 获取错误提示信息
msg = driver.find_element_by_css_selector(".layui-layer-content").text
print(msg)

# 关闭浏览器驱动
time.sleep(5)
driver.quit()
  1. 登录功能, 密码错误
# 密码错误
import time
from selenium import webdriver

# 实例化浏览器驱动
driver = webdriver.Chrome()
driver.maximize_window()
driver.implicitly_wait(10)
driver.get("http://localhost/")

# 1. 点击首页的"登录"链接, 进入登录页面
driver.find_element_by_class_name("red").click()
# 2. 输入用户名
driver.find_element_by_id("username").send_keys("17150312012")
# 3. 输入一个错误密码
driver.find_element_by_id("password").send_keys("error")
# 4. 输入验证码
driver.find_element_by_id("verify_code").send_keys("8888")
# 5. 点击登录按钮
driver.find_element_by_name("sbtbutton").click()
# 6. 获取错误提示信息
msg = driver.find_element_by_css_selector(".layui-layer-content").text
print(msg)

# 关闭浏览器驱动
time.sleep(5)
driver.quit()

存在的问题

  • 一条测试用例对应一个文件, 用例多时, 不方便维护管理
  • 代码高度冗余

4.4 V2版本

引入pytest管理测试用例, 并断言用例的执行结果

好处

  • 方便组织和管理多个测试用例
  • 提供了丰富的断言方法
  • 方便生成测试报告
  • 减少了代码冗余

示例代码

# 导包
import time
from selenium import webdriver

# 定义测试类
class TestLogin:

    def setup(self):
        self.driver = webdriver.Chrome()
        self.driver.maximize_window()
        self.driver.implicitly_wait(10)
        self.driver.get("http://localhost/")

    def teardown(self):
        time.sleep(5)
        self.driver.quit()

    # 定义用户不存在的测试方法
    def test_login_account_not_exist(self):
        # 1. 点击首页的"登录"链接, 进入登录页面
        self.driver.find_element_by_class_name("red").click()
        # 2. 输入一个不存在的用户名
        self.driver.find_element_by_id("username").send_keys("18800000000")
        # 3. 输入密码
        self.driver.find_element_by_id("password").send_keys("123456")
        # 4. 输入验证码
        self.driver.find_element_by_id("verify_code").send_keys("8888")
        # 5. 点击登录按钮
        self.driver.find_element_by_name("sbtbutton").click()
        # 6. 获取错误提示信息
        msg = self.driver.find_element_by_css_selector(".layui-layer-content").text
        print(msg)
        # 断言
        assert "账号不存在!" == msg

    # 定义密码错误的测试方法
    def test_login_password_error(self):
        # 1. 点击首页的"登录"链接, 进入登录页面
        self.driver.find_element_by_class_name("red").click()
        # 2. 输入用户名
        self.driver.find_element_by_id("username").send_keys("17150312012")
        # 3. 输入一个错误密码
        self.driver.find_element_by_id("password").send_keys("error")
        # 4. 输入验证码
        self.driver.find_element_by_id("verify_code").send_keys("8888")
        # 5. 点击登录按钮
        self.driver.find_element_by_name("sbtbutton").click()
        # 6. 获取错误提示信息
        msg = self.driver.find_element_by_css_selector(".layui-layer-content").text
        print(msg)
        # 断言
        assert "密码错误!" == msg

存在问题

依然是代码冗余

4.4.1 方法封装

概念

是将一些有共性的或多次被使用的代码提取到一个方法中, 供其他地方调用

好处

  • 避免代码冗余
  • 容易维护
  • 隐藏代码实现的细节

目的

用最少的代码实现最多的功能

4.5 V3版本

驱动工具类

# 获取/关闭浏览器驱动的类
from selenium import webdriver


class DriverUtils:
    __driver = None

    # 获取浏览器驱动
    @classmethod
    def get_driver(cls):
        if cls.__driver is None:
            cls.__driver = webdriver.Chrome()
            cls.__driver.maximize_window()
            cls.__driver.implicitly_wait(10)
        return cls.__driver

    # 关闭浏览器驱动
    @classmethod
    def quit_driver(cls):
        if cls.__driver is not None:
            cls.__driver.quit()
            cls.__driver = None

测试类

# 导包
import time
from v3.driver_utils_121 import DriverUtils

# 定义测试类
class TestLogin:

    def setup(self):
        self.driver = DriverUtils.get_driver()
        self.driver.get("http://localhost/")

    def teardown(self):
        time.sleep(5)
        DriverUtils.quit_driver()

    # 定义用户不存在的测试方法
    def test_login_account_not_exist(self):
        # 1. 点击首页的"登录"链接, 进入登录页面
        self.driver.find_element_by_class_name("red").click()
        # 2. 输入一个不存在的用户名
        self.driver.find_element_by_id("username").send_keys("18800000000")
        # 3. 输入密码
        self.driver.find_element_by_id("password").send_keys("123456")
        # 4. 输入验证码
        self.driver.find_element_by_id("verify_code").send_keys("8888")
        # 5. 点击登录按钮
        self.driver.find_element_by_name("sbtbutton").click()
        # 6. 获取错误提示信息
        msg = self.driver.find_element_by_css_selector(".layui-layer-content").text
        print(msg)
        # 断言
        assert "账号不存在!" == msg

    # 定义密码错误的测试方法
    def test_login_password_error(self):
        # 1. 点击首页的"登录"链接, 进入登录页面
        self.driver.find_element_by_class_name("red").click()
        # 2. 输入用户名
        self.driver.find_element_by_id("username").send_keys("17150312012")
        # 3. 输入一个错误密码
        self.driver.find_element_by_id("password").send_keys("error")
        # 4. 输入验证码
        self.driver.find_element_by_id("verify_code").send_keys("8888")
        # 5. 点击登录按钮
        self.driver.find_element_by_name("sbtbutton").click()
        # 6. 获取错误提示信息
        msg = self.driver.find_element_by_css_selector(".layui-layer-content").text
        print(msg)
        # 断言
        assert "密码错误!" == msg

注意: 如果想要引用其他类, 那么被引用的类的文件名要符合要求, 比如不能出现 []

存在的问题

代码冗余

4.6 PO模式

在做UI自动化时, 元素定位特别依赖页面, 如果页面变更, 自动化脚本就需要被修改

存在的问题

  • 如果前端工程师改了某个元素, 你就得修改所有对应的代码
  • 存在大量的冗余

如果解决?

答案就是 PO模式

4.6.1 概念

PO是Page Object的缩写, PO模式是自动化测试开发的最佳设计模式之一

核心思想:

  • 通过对页面元素的封装减少冗余代码, 同时在后期维护中, 若元素发生变化, 只需要调整页面元素封装的代码即可, 提高了测试用例的可维护性, 可读性
  • 页面和测试脚本分离

4.6.2 PO模式分层

分层机制, 让不同层去做不同类型的事情, 让代码结构清晰, 增加复用性

分层方式

  1. 两层: 对象操作层 + 业务数据层

    • 对象操作层: 封装页面信息, 包括元素以及元素的操作
    • 业务数据层: 封装多种操作组合的业务以及测试数据
  2. 三层: 对象库 + 操作层 + 业务数据层 / 对象操作层 + 业务层 + 数据层

  3. 四层: 对象库 + 操作层 + 业务层 + 数据层

4.6.3 PO模式优点

  • 引入PO模式前
    • 存在大量冗余代码
    • 业务流程不清晰
    • 后期维护成大
  • 引入PO模式后
    • 减少冗余代码
    • 业务代码和测试数据被分开, 降低耦合性
    • 维护成本低

4.7 V4版本

介绍

采用PO模式的分层思想对代码进行拆分

PO封装

对登录页面进行封装: 封装到类 LoginPage

对测试用例进行封装: 封装到类 TestLogin

代码结构

  • utils包

    • driver_utils.py
  • page包

    • login_page.py
  • scripts包

    • test_login.py
  • pytest.ini

PO封装

login_page.py

class LoginPage:

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

    # 点击首页的"登录"链接, 进入登录页面
    def click_login_link(self):
        return self.driver.find_element_by_class_name("red").click()

    # 输入用户名
    def input_username(self, username):
        return self.driver.find_element_by_id("username").send_keys(username)

    # 输入密码
    def input_password(self, password):
        return self.driver.find_element_by_id("password").send_keys(password)

    # 输入验证码
    def input_verify_code(self, code):
        return self.driver.find_element_by_id("verify_code").send_keys(code)

    # 点击登录按钮
    def click_login_btn(self):
        return self.driver.find_element_by_name("sbtbutton").click()

    # 获取提示信息
    def get_msg(self):
        msg = self.driver.find_element_by_css_selector(".layui-layer-content").text
        return msg

test_login.py

class LoginPage:

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

    # 点击首页的"登录"链接, 进入登录页面
    def click_login_link(self):
        return self.driver.find_element_by_class_name("red").click()

    # 输入用户名
    def input_username(self, username):
        return self.driver.find_element_by_id("username").send_keys(username)

    # 输入密码
    def input_password(self, password):
        return self.driver.find_element_by_id("password").send_keys(password)

    # 输入验证码
    def input_verify_code(self, code):
        return self.driver.find_element_by_id("verify_code").send_keys(code)

    # 点击登录按钮
    def click_login_btn(self):
        return self.driver.find_element_by_name("sbtbutton").click()

    # 获取提示信息
    def get_msg(self):
        msg = self.driver.find_element_by_css_selector(".layui-layer-content").text
        return msg

4.8 v5版本

介绍

对PO分层后的代码继续优化

优化内容

  • 分离page页面中的元素和操作
  • 优化元素定位方式

示例代码

login_page.py

from selenium.webdriver.common.by import By


class LoginPage:

    # 登录链接 按钮
    login_link_btn = By.CLASS_NAME, "red"
    # 用户名 输入框
    username_input = By.ID, "username"
    # 密码 输入框
    password_input = By.ID, "password"
    # 验证码 输入框
    verify_code_input = By.ID, "verify_code"
    # 登录 按钮
    login_btn = By.NAME, "sbtbutton"
    # 提示信息
    msg_info = By.CSS_SELECTOR, ".layui-layer-content"

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

    def find_el(self, feature):
        return self.driver.find_element(*feature)
        # return self.driver.find_elment(feature[0], feature[1])

    # 点击首页的"登录"链接, 进入登录页面
    def click_login_link(self):
        return self.find_el(self.login_link_btn).click()
        # return self.driver.find_elment(self.login_link_btn[0], self.login_link_btn[1]).click()
        # return self.driver.find_element_by_class_name("red").click()

    # 输入用户名
    def input_username(self, username):
        return self.find_el(self.username_input).send_keys(username)
        # return self.driver.find_element_by_id("username").send_keys(username)

    # 输入密码
    def input_password(self, password):
        return self.find_el(self.password_input).send_keys(password)
        # return self.driver.find_element_by_id("password").send_keys(password)

    # 输入验证码
    def input_verify_code(self, code):
        return self.find_el(self.verify_code_input).send_keys(code)
        # return self.driver.find_element_by_id("verify_code").send_keys(code)

    # 点击登录按钮
    def click_login_btn(self):
        return self.find_el(self.login_btn).click()
        # return self.driver.find_element_by_name("sbtbutton").click()

    # 获取提示信息
    def get_msg(self):
        return self.find_el(self.msg_info).text
        # msg = self.driver.find_element_by_css_selector(".layui-layer-content").text
        # return msg

4.9 v6版本

介绍

把共同的方法进行封装

优化内容

  • 封装操作基类
    • 封装查找元素的方法
    • 封装基本操作方法: 点击/ 清空/ 输入等等
  • page继承操作基类

结构

  • utils包

    • driver_utils.py
  • page包

    • login_page.py
  • scripts包

    • test_login.py
  • pytest.ini

  • base包

    • base_action.py

4.9.1 示例代码

base_action.py

class BaseAction:

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

    def find_el(self, feature):
        return self.driver.find_element(*feature)

    def find_els(self, feature):
        return self.driver.find_elements(*feature)

    def click(self, feature):
        return self.find_el(feature).click()

    def input(self, feature, content):
        return self.find_el(feature).send_keys(content)

    def clear(self, feature):
        return self.find_el(feature).clear()

注意: page页面要继承 BaseAction

5 数据驱动

概念

是以数据来驱动整个测试用例的执行, 也就是测试数据决定测试结果

特点

  • 可以把数据驱动理解为一种模式或者一种思想
  • 数据驱动技术可以让用户把关注点放在测试数据的构建和维护上, 而不是直接维护脚本, 可以利用同样的过程, 对不同的输入数据进行测试
  • 数据驱动要依赖参数化技术

数据来源

  • 直接定义在测试脚本中 (简单直观, 但测试方法和测试数据未分离, 不方便后期维护)
  • 从文件中读取数据, 如 txt, excel, xml, JSON等格式文件
  • 从数据库读取数据

5.1 JSON基本介绍

概念

JSON全称是" JavaScript Object Notation", 是JavaScript 对象表示法, 它是一种基于文本, 独立于语言的轻量级数据交换格式

特点

  • JSON是纯文本
  • JSON具有良好的自我描述性, 便于阅读和编写
  • JSON具有清晰的层级结构
  • 有效的提升网络传输效率

对比XML

  • XML指可扩展标记语言, 被设计用来传输数据
  • 如果使用XML, 需要读取XML, 然后通过标签结点来遍历文档, 并读取对应的值, 然后传输
  • 使用JSON, 只需要读取JSON字符串

JSON语法规格

  • 大括号保存对象
  • 中括号保存数组
  • 对象和数组可以相互嵌套
  • 数据采用键值对来表示
  • 多个数据用逗号分隔

JSON值

  • 数字 (整数或者浮点数)
  • 字符串 (在双引号中)
  • 逻辑值 (true 或者 false)
  • 数组 (在中括号中)
  • 对象 (在大括号中)
  • null
    • JSON中空值用 null 表示
    • python中对应的用 None 表示

JSON基本操作

操作内容

  • python字典与JSON之间的转换

  • JSON文件读写

    在python中想要操作 JSON, 需要先导入依赖包

    import json
    

5.2 字典与JSON转换

代码

import json

# 把python字典类型转换为JSON字符串
dict1 = {
    "name": "zhangsan",
    "age": 18,
    "is_man": True,
    "school": None
}
# 使用 dumps 方法, 得到的结果是 json 字符串
json_str1 = json.dumps(dict1)
print(json_str1)

# 把JSON字符串转换为python字典
json_str2 = '{"name": "zhangsan", "age": 18, "is_man": true, "school": null}'
# 使用 loads 方法, 得到的结果是 python字典
dict2 = json.loads(json_str2)
print(dict2)

把python字典类型转换为JSON字符串: 使用 dumps 方法

把JSON字符串转换为python字典: 使用 loads 方法

5.3 JSON文件读写

代码

import json

# 读取 data.json 文件
with open("data.json", "r", encoding="utf-8") as f:
    data1 = json.load(f)
    print(data1)

# 把字典写入json文件 "data2.json"
data2 = data1
with open("data2.json", "w", encoding="utf-8") as f:
    json.dump(data2, f)

# 把字典写入json文件 "data3.json"  ------解决写入中文的问题
data3 = data1
with open("data3.json", "w", encoding="utf-8") as f:
    json.dump(data2, f, ensure_ascii=False)

实现步骤

  1. 编写测试用例
  2. 敲代码
    1. 采用PO模式的分层思想对页面进行封装
    2. 编写测试脚本
    3. 定义数据文件, 实现参数化

6 项目实战

在线计算器项目 http://cal.apple886.com/

6.1 项目结构

  • base ----> 存储页面对象的父类(便于子类调用)
  • data ----> 存储测试用例数据
  • page ----> 存储页面对象
  • scripts ----> 存储测试脚本
  • utils ----> 存储经常使用的工具类
  • pytest.ini ----> 运行项目的配置

6.2 base包

新建base_action.py

class BaseAction:
    # 初始化驱动
    def __init__(self, driver):
        self.driver = driver

    # 查找单个元素
    def find_el(self, feature):
        return self.driver.find_element(*feature)

    # 查找多个元素
    def find_els(self, feature):
        return self.driver.find_elements(*feature)

    # 查找按钮元素
    def click(self, feature):
        return self.find_el(feature).click()

    # 查找输入元素
    def input(self, feature, content):
        return self.find_el(feature).send_keys(content)

    # 清空
    def clear(self, feature):
        return self.find_el(feature).clear()

    # 定位数字按钮
    def find_el_num(self, feature, num):
        # 将num格式化为字符串
        return self.driver.find_element(feature[0], feature[1].format(str(num)))

6.3 data包

新建cal_data.json

{
    "cal_001": {
        "data": [1, 3],
        "result": 4
    },
    "cal_002": {
        "data": [1, 2],
        "result": 3
    },
     "cal_002": {
        "data": [1, 2, 3],
        "result": 6
    }
}

6.4 page包

新建cal_page.py

from selenium.webdriver.common.by import By

from base.base_action import BaseAction


class CalPage(BaseAction):

    # 数字按钮
    number_btn = By.ID, "simple{}"
    # 加号按钮
    add_btn = By.ID, "simpleAdd"
    # 等号按钮
    equal_btn = By.ID, "simpleEqual"
    # 结果
    result = By.ID, "resultIpt"

    # 点击数字
    def click_number_btn(self, num):
        return self.find_el_num(self.number_btn, num).click()

    # 点击加号
    def click_add_btn(self):
        return self.click(self.add_btn)

    # 点击等于号
    def click_equal_btn(self):
        return self.click(self.equal_btn)

    # 显示结果
    def get_result(self):
        return self.find_el(self.result).get_attribute("value")

6.5 scripts包

新建tesst_cal.py

# 导包
import time
import pytest
from page.cal_page import CalPage
from utils.driver_utils import DriverUtils
from utils.read_data import read_data


# 定义测试类
class TestCal:
    def setup_method(self):
        self.driver = DriverUtils.get_driver()
        self.cal_page = CalPage(self.driver)
        self.driver.get("http://cal.apple886.com/")

    def teardown_method(self):
        time.sleep(5)
        DriverUtils.quit_driver()

    # 加法算数
    @pytest.mark.parametrize("params", read_data("cal_data.json"))
    def test_2_add(self, params):
        for i in params["data"]:
            self.cal_page.click_number_btn(i)
            self.cal_page.click_add_btn()
        time.sleep(5)
        # 4. 点击等于号按钮
        self.cal_page.click_equal_btn()
        time.sleep(5)
        # 断言
        assert str(params["result"]) == self.cal_page.get_result()

6.6 utils包

新建driver_utils.py

# 获取/关闭浏览器驱动的类
from selenium import webdriver


class DriverUtils:
    __driver = None

    # 获取浏览器驱动
    @classmethod
    def get_driver(cls):
        if cls.__driver is None:
            cls.__driver = webdriver.Chrome()
            cls.__driver.maximize_window()
            cls.__driver.implicitly_wait(10)
        return cls.__driver

    # 关闭浏览器驱动
    @classmethod
    def quit_driver(cls):
        if cls.__driver is not None:
            cls.__driver.quit()
            cls.__driver = None

新建read_data.py用于读取data包的数据

# 读取data数据文件
import json


def read_data(filename):
    with open("./data/" + filename, "r", encoding="utf-8") as f:
        list_data = []
        dict_list = json.load(f)
        for value in dict_list.values():
            list_data.append(value)
        return list_data

在这里插入图片描述

7 日志收集

7.1 日志收集

概念 : 日志就是用于记录系统运行时的信息, 也称为Log

作用:

  • 调试程序
    • 旧的方式: print(“xxxx”)
      • low
    • 新的方式: 通过日志
  • 了解程序运行的情况, 是否正常
  • 程序运行故障分析与问题定位
  • 用来做用户行为分析和数据统计
    • 需要学好 sql

级别:

  • 思考
    • 是否记录的所有日志信息重要性都一样?
  • 日志级别, 指日志信息的重要性

常见日志级别:

  1. DEBUG === 调试
  2. INFO === 信息
  3. WARNING === 警告
  4. ERROR === 错误
  5. CRITICAL === 严重错误

日志基本用法:

  • logging:python中有一个标准库, logging模块可以直接记录日志

  • 使用

    1. 导入 logging 包
    2. 输出日志
    3. 默认的日志级别被设置为 warning
  • 设置日志级别

    • 方法
      • logging.basicConfig(level=logging.DEBUG)
  • 设置日志格式

    • 默认格式
      • 日志级别 : Logger名称 : 日志内容
    • 自定义格式
      • logging.basicConfig(format="xxxxxx")
  • 将日志信息输出到文件

    • 默认
      • python的logging模块将日志打印到了标准输出中(控制台)
    • 将日志输出到文件的方法
      • logging.basicConfig(filename=“xxx.log”)

7.2 日志高级用法

思考

  • 如何将日志信息同时输出到控制台和日志文件中?
  • 如何将不同级别的日志输出到不同的日志文件?
  • 如何解决日志文件过大的问题?

7.3 四大组件

  1. 日志器(Logger):提供了程序使用日志的入口
  2. 处理器(Handler):将logger创建的日志记录发送到合适的输出
  3. 格式器(Formatter):决定日志的输出格式
  4. 过滤器(Filter): 提供了更细粒度的控制工具来决定输出哪条日志记录, 丢弃哪条日志记录

组件之间的关系

  • 日志器 (Logger) 是入口,
  • 真正干活的是处理器 (Handler),
  • 处理器还可以通过格式器 (Formatter)
  • 过滤器 (Filter) 对输出的日志内容做格式化和过滤

7.3.1 Logger类

  • 如何创建Logger对象

    • logger = logging.getLogger(name)

    • 可选参数 name

      • 如果不写name, 日志器名称默认为 root

      • 如果写了name, 如, logger = logging.getLogger(“myLogger”) 那么日志器的名称为 myLogger

Logger常用方法

  • 打印日志

    • logger.debug()
    • logger.info()
    • logger.warning
    • logger.error
    • logger.critical()

    设置日志级别

    • logger.setLevel()
    • 为logger对象添加一个handler对象
      • logger.addHandler()

    为logger对象添加一个filter对象

    • logger.addFilter

7.3.2 Handler类

  • 如何创建Handler对象
    • 在程序中不应该直接实例化和使用Handler实例, 因为Handler是一个基类, 它只定义了Handler应该有的接口, 应该使用Handler实现类来创建对象
    • 创建方式
      • 输出日志到控制台
        • logging.StreamHandler
      • 输出到磁盘文件, 默认文件大小会无限增长
      • 输出到文件, 按文件大小切割
      • 输出到文件, 按时间切割
        • logging.hanlders.TimedRotatingFileHandler
      • 将日志消息以get或post的方式发送给http服务器
      • 将日志消息发送给一个指定的email地址
    • 常用方法
      • 为handler设置格式器对象
        • handler.setFormatter()

7.3.3 Formatter类

**作用:**Formatter对象用于配置日志信息的格式

  • 如何创建Formatter对象

    • logging.Formatter(fmt=None, datefmt=None) fmt: 消息格式化字符串, 如果不指定该参数则默认使用message的原始值 datefmt: 日期格式化字符串, 如果不指定该参数则默认使用 “%Y-%m-%d %H:%M:%S”
  • 案例

    • 说明

      • 可读性好的日志需要具备一些特征

        • 在控制台和文件都能输出
        • 文件输出能够按时间切割

步骤

  1. 导包
  2. 创建日志器对象 / 设置日志级别
  3. 创建处理器对象: 输出到控制台 + 文件(按时间切割)
  4. 创建格式器对象
  5. 将格式器添加到处理器
  6. 将处理器添加到日志器
  7. 打印日志

代码

# 1. 导包
import logging
import logging.handlers
# 2. 创建日志器对象 / 设置日志级别
logger = logging.getLogger()      # 默认日志器名称为 root
# logger = logging.getLogger("An")   # 自定义日志器名称为 An
logger.setLevel(level=logging.DEBUG)
# 3. 创建处理器对象: 输出到控制台 + 文件(按时间切割)
ls = logging.StreamHandler()
lf = logging.handlers.TimedRotatingFileHandler(filename="172.log", when="s", backupCount=3)
# 4. 创建格式器对象
fmt = "%(asctime)s %(levelname)s [%(name)s] [%(filename)s:%(funcName)s:%(lineno)d] - %(message)s"
formatter = logging.Formatter(fmt=fmt)
# 5. 将格式器添加到处理器
ls.setFormatter(formatter)
lf.setFormatter(formatter)
# 6. 将处理器添加到日志器
logger.addHandler(ls)
logger.addHandler(lf)
# 7. 打印日志
while 1:
    logger.debug("===================================================================")

8 面试题

  1. 说明样的项目适合做web自动化?

①需求变动不频繁

②项目周期长

③项目需要回归测试

  1. web自动化一个什么时候开始?

①手工测试结束后

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【AI实践】Dify开发应用和对接微信

自定义应用 创建应用有2种&#xff0c; 从应用模板创建 空白应用&#xff0c;也就是自定义应用 选择翻译助手 Translation assistant模板创建一个应用 自定义应用&#xff0c;创建一个child_accompany_bot自定的应用&#xff0c;用来支持家长&#xff0c;如何解决低龄儿童的…

马克·雷伯特访谈:机器人的未来及波士顿动力的创新之路

引言 机器人技术作为现代科技的前沿领域&#xff0c;始终吸引着大量的关注与研究。波士顿动力公司作为这一领域的领军者&#xff0c;其创始人兼前CEO马克雷伯特&#xff08;Marc Raibert&#xff09;近日在主持人莱克斯弗里德曼&#xff08;Lex Fridman&#xff09;的播客节目…

this指针如何使C++成员指针可调用

在C中&#xff0c;this指针是一个隐藏的指针&#xff0c;指向当前对象实例。它在成员函数中自动可用&#xff0c;用于访问该对象的成员变量和成员函数。理解this指针的工作原理有助于理解为什么指向成员的指针是可调用的。在本文中&#xff0c;我们将详细探讨this指针的概念&am…

计算机组成原理之定点运算器的组成

文章目录 定点运算器的组成逻辑运算ALU两级先行进位的ALU 总线单总线结构双总线结构三总线结构 定点运算器的组成 逻辑运算 总的来说&#xff0c;逻辑非运算就是按位取反&#xff1b;逻辑加运算就是按位取或运算&#xff1b;逻辑乘运算就是按位取和运算&#xff1b;逻辑异运算…

vue框架学习------框架概述

框架 在基础语言之上,对各种基础功能进行封装 什么是框架 框架&#xff08;Framework&#xff09;是整个或部分系统的可重用设计&#xff0c;表现为一组抽象构件及构件实例间交互的方法; 另一种定义认为&#xff0c;框架是可被应用开发者定制的应用骨架。前者是从应用方面而…

分类模型部署-ONNX

分类模型部署-ONNX 0 引入&#xff1a;1 模型部署实战测试&#xff1a;1 安装配置环境&#xff1a;2 Pytorch图像分类模型转ONNX-ImageNet1000类3 推理引擎ONNX Runtime部署-预测单张图像&#xff1a; 2 扩展阅读参考 0 引入&#xff1a; 在软件工程中&#xff0c;部署指把开发…

仅凭一图,即刻定位,AI图像定位技术

AI图像定位技术&#xff0c;解锁空间密码&#xff01;仅凭一图&#xff0c;即刻定位&#xff0c;精准至经纬度坐标&#xff0c;让世界无处不晓。 试试看能否猜中这张自拍照的背景所在&#xff1f;可别低估了A的眼力&#xff0c;答案说不定会让你大吃一惊呢。 近期&#xff0c;…

计算机组成原理之浮点四则运算

文章目录 浮点加减运算浮点乘法运算浮点除法运算浮点运算器的流水原理习题 浮点加减运算 总的来说&#xff0c;分为四个步骤&#xff1a; &#xff08;1&#xff09;0操作数检查 &#xff08;2&#xff09;比较阶码大小并完成对阶 &#xff08;3&#xff09;尾数进行加或者减操…

异常向量表的设置

1、Linux Kernel中对异常向量表的填充 linux/arch/arm64/kernel/entry.S kernel_ventry 是一个定义异常向量的宏&#xff1b; 在该宏中&#xff0c;程序跳转到了b el\el\ht()\regsize()\label; 以为异常向量的第6行为例&#xff0c;其实就是跳转到了bl el1h_64_irq; 然后你去搜…

算法题解记录28+++对称二叉树(百日筑基)

一、题目描述&#xff1a; 题目难度&#xff1a;简单 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 分割线 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,nul…

Python基础用法 之 数据类型

Python常见数据类型分类 数字型非数字型整型&#xff1a; 整数--int--16 字符串&#xff1a;使用引号引起来的的就是字符串--Tom 浮点型&#xff1a;小数--float--16.66列表&#xff1a;list [1,2,3] 布尔型&#xff1a;bool&#xff08;真True&#xff0c;假False&#xff…

手写MyBatis 重要基本原理框架

1. 手写MyBatis 重要基本原理框架 文章目录 1. 手写MyBatis 重要基本原理框架1.1 第一步&#xff1a;IDEA中创建模块1.2 第二步&#xff1a;资源工具类&#xff0c;方便获取指向配置文件的输入流1.3 第三步&#xff1a;定义SqlSessionFactoryBuilder类1.4 第四步&#xff1a;分…

国产MCU芯片(2):东软MCU概览

前言: 国产芯片替代的一个主战场之一就是mcu,可以说很多国内芯片设计公司都打算或者已经在设计甚至有了一款或多款的量产产品了,这也是国际大背景决定的。过去的家电市场、过去的汽车电子市场,的确国产芯片的身影不是很常见,如今不同了,很多fabless投身这个行业,一种是…

开源语音合成模型ChatTTS本地部署结合内网穿透实现远程访问

文章目录 前言1. 下载运行ChatTTS模型2. 安装Cpolar工具3. 实现公网访问4. 配置ChatTTS固定公网地址 前言 本篇文章就教大家如何快速地在Windows中本地部署ChatTTS&#xff0c;并且我们还可以结合Cpolar内网穿透实现公网随时随地访问ChatTTS AI语言模型。 最像人声的AI来了&a…

跟着刘二大人学pytorch(第---12---节课之RNN基础篇)

文章目录 0 前言0.1 课程视频链接&#xff1a;0.2 课件下载地址&#xff1a; 1 Basic RNN1.1 复习DNN和CNN1.2 直观认识RNN1.3 RNN Cell的内部计算方式 2 具体什么是一个RNN&#xff1f;3 使用pytorch构造一个RNN3.1 手动构造一个RNN Cell来实现RNN3.2 直接使用torch中现有的RN…

(4)SDL渲染开发

SDL渲染开发 1. SDL简介2. 环境搭建2.1 windows2.2 Linux 3. SDL子系统3.1 eg1 创建窗口 4&#xff1a;SDL显示4.1 显示图片4.2 绘制长方形显示 5. SDL事件6. SDL多线程6.1 接口演示6.2 yuv显示6.3 pcm播放 1. SDL简介 SDL&#xff08;Simple DirectMedia Layer&#xff09;是一…

数据库概述1

数据&#xff1a;描述事物的符号记录称为数据&#xff1b; 包括数字、图片、音频等&#xff1b; 数据库&#xff1a;长期储存在计算机内有组织、可共享的大量数据的集合&#xff1b;数据库中的数据按照一定的数据模型组织、描述和存储&#xff0c;具有较小的数据冗余、较高的数…

【支持向量机】问题梳理

学完支持向量机后我有些地方不太清楚&#xff0c;故做如下梳理&#xff1a; 1.为什么支持向量机模型认为一个点划分正确的标志是y(wxb)>1呢&#xff0c;为什么不是y(wxb)>0&#xff0c;比如y为1&#xff0c;wxb为0.5&#xff0c;大于0&#xff0c;则预测正确。 2.所以意思…

[Python]Anaconda相关命令

环境操作相关命令 查看所有环境 conda env list创建环境 conda create --name cahttts python3.10激活环境 conda activate cahttts安装依赖文件 pip install -r requirements.txt查看GPU型号 nvidia-smi -LGPU 0: NVIDIA A10 (UUID: GPU-9f1fc9cf-582a-25ac-849c-2f77343…

FFmpeg编解码的那些事(3)-视频硬解码的基础知识

目录 前言&#xff1a; 1.iso/os x平台 2.windows平台 3.linux平台 4.Tips&#xff1a; 5.结论&#xff1a; 前言&#xff1a; 视频硬解码的过程就是把视频提取成图片变显示出来&#xff0c;就是播放器播放视频的过程&#xff0c;就可以理解为解码的过程。 在不同的系统…