《一》理论知识
(1)UnitTest测试框架:用例的管理框架,UnitTest是目前业内主流的自动化测试框架之一
(2)市场现在主要进行用例管理的测试代码基本上都是基于UnitTest/PyTest来实现的,主要是用于接口的管理用例
(3)环境的搭建:啥都不用干,因为UnitTest是默认安装的官方库。直接调用即可
《二》UnitTest的应用
(1)通过unittest.TestCase的继承,让UnitTest生效
(2)测试用例的定义都是基于函数的形态,并且函数命名必须以test开头。推荐以test_开头会更加规范
(3)测试用例的运行顺序是基于ascll码来执行的,基本顺序是0-9、A-Z,a-z
(4)在UnitTest中是可以支持普通函数的定义
(5)前置后置的调用:一般只用于做初始化与资源释放。不接受参数的传递
因为前置和后置只能够编写一次。写多了就会被新写的覆盖。
如果每一个测试用例的前置都不一样,那就表明你setup是运行不了的。
基于cls对象生成的class级别前后置:需要添加@classmethod进行标注
(6)一个py文件是可以有多套UnitTest对象存在的(即可有多个class)
(7)除非必要,一般一个class管理一套测试用例即可。一个py文件对应一套class
(8)类文件执行顺序
1. main
2. 继承于UnitTest.TestCase类的class
3. setupClass(一个class对象只执行一次)
4. setup(每个测试用例都执行一次)
5. 测试用例
6. teardown(每个测试用例都执行一次)
7. teardownClass(一个class对象只执行一次)
(9)unittest_demo.py(源码)
import unittest
# 调用UnitTest
class TestDemo(unittest.TestCase):
# 定义Class前置
@classmethod
def setUpClass(cls) -> None:
print('这是class setup函数')
# 定义Class后置
@classmethod
def tearDownClass(cls) -> None:
print('这是class teardown函数')
# 定义前置:测试用例的前置,每一个用例执行前都会执行该前置
def setUp(self):
print('这是setup用例前置函数')
# 定义后置:测试用例的后置,每一个测试用例执行完毕后都会执行该后置
def tearDown(self):
print('这是tearDown用例后置函数')
# 测试用例1
def test_02_login(self):
print('第二条用例执行开始......')
print('这是test_login测试用例')
# 测试用例2
def test_01(self):
print('第一条用例执行开始......')
print('这是test_1测试用例')
# 测试用例3
def test_03_Login(self):
print('第三条用例执行开始......')
print('这是test_Login测试用例')
# 普通函数
def temp(self):
print('这是temp函数')
# 测试用例4
def test_04(self):
print('第四条用例执行开始......')
print('这是test_04')
self.temp()
class TestDemo1(unittest.TestCase):
def test_01(self):
print('这是testdemo1中的test01函数')
# 执行UnitTest
if __name__ == '__main__':
unittest.main()
《三》基于UnitTest实现关键字驱动的测试操作:实现登录
(1)断言方法:UnitTest中所有的断言函数都是self.assert*
(2)测试用例标注为失败:1. 断言失败导致测试用例失败 2. 用例内容运行时报错导致用例失败
(3)测试用例中不要添加try except来处理异常
(4)基于不同的场景来定义选择对应的前后置方式
如果需要独立各个用例,就是用case级别
如果需要用例之间管理缓存信息,则用class级别
(5)UnitTest支持在多用例的情况下单独运行指定测试用例,但是不推荐,因为可能会报错
(6)unittest_auto.py(源码)
import unittest
from test008.test0084.test0084_01.keys import Keys
class TestCase(unittest.TestCase):
@classmethod
def setUpClass(cls) -> None:
cls.key = Keys('Chrome')
@classmethod
def tearDownClass(cls) -> None:
cls.key.quit()
# def setUp(self) -> None:
# self.key = Keys('Chrome')
#
# def tearDown(self) -> None:
# self.key.quit()
# 实现登录操作流程
def test_01_login(self):
self.key.open('http://39.98.138.157/shopxo/index.php')
self.key.click('link text', '登录')
self.key.input('name', 'accounts', 'xuzhu666')
self.key.input('name', 'pwd', '123456')
self.key.click('xpath', '//button[text()="登录"]')
self.key.wait(3)
# 第一种方式进行断言
# status = self.key.assert_text('link text', '退出', '退出')
# self.assertTrue(status, msg='断言失败,{}不为True'.format(status))
# 第二种方式进行断言
self.assertEqual(first='退出',
second=self.key.get_text('link text', '退出'),
msg='断言失败')
# 实现商品搜索流程
def test_02_search(self):
self.key.open('http://39.98.138.157/shopxo/index.php')
self.key.input('name', 'wd', '手机')
self.key.click('id', 'ai-topsearch')
self.key.wait(3)
if __name__ == '__main__':
unittest.main()
《四》keys.py(源码)
'''
Selenium关键字驱动类:常用操作行为给封装为各类关键字。
所有的函数,如果不添加return,最后会返回None
将常用的自行封装到自定义类中,在使用时,直接调用自定义封装的类即可。
(1)创建临时driver (2)构造函数 (3)访问url
(4)定位元素 (5)点击操作 (6)输入 (7)退出 (8)显式等待 (9)强制等待
(10)切换Iframe (11)切换default窗体 (12)相对定位器 (13)句柄的切换 (14)断言文本信息
'''
# 自定义关键字驱动类
from time import sleep
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from test008.test0083.ui_demo.conf.chrome_options import ChromeOptions
# 基于type_值决定生成的driver对象是什么类型
def open_browser(type_):
if type_ == 'Chrome':
driver = webdriver.Chrome(options=ChromeOptions().options())
else:
try:
driver = getattr(webdriver, type_)()
# 等同于 getattr(txt='Chrome')
except Exception as e:
print("Exception Information:" + str(e))
# 默认启动Chrome的浏览器
driver = webdriver.Chrome()
return driver
'''
Python反射机制:
四大内置函数:常用的是其中的getattr函数,就是获取指定类的指定属性
getattr(类,属性) 相当于 类.属性 的意思
例如:
webdriver.Chrome == getattr(webdriver,'Chrome')
基于反射获取属性是这个反射函数的基本定义。获取函数就需要在末尾加上()
例如:
webdriver.Chrome() == getattr(webdriver,'Chrome')()
例如open_browser()函数:
不用反射的形态
if type_ == Chrome:
driver = webdriver.Chrome()
elif type_ == Firefox:
driver = webdriver.Firefox()
elif type_ == Ie:
driver = webdriver.Ie()
elif safari
elif edge
'''
class Keys:
# 创建临时driver:driver = webdriver.Chrome()
# 构造函数
def __init__(self, txt):
self.driver = open_browser(txt)
self.driver.implicitly_wait(10)
# 访问url
def open(self, txt):
self.driver.get(txt)
# 定位元素
def locate(self, name, value):
return self.driver.find_element(name, value)
# 输入
def input(self, name, value, txt):
self.locate(name, value).send_keys(txt)
# 点击操作
def click(self, name, value):
self.locate(name, value).click()
# 退出
def quit(self):
self.driver.quit()
# 显式等待
def web_el_wait(self, name, value):
return WebDriverWait(self.driver, 10, 0.5).until(
lambda el: self.locate(name, value), message='元素查找失败')
# 强制等待
def wait(self, txt):
sleep(txt)
# 切换Iframe
def switch_frame(self, value, name=None):
if name is None:
self.driver.switch_to.frame(value)
else:
self.driver.switch_to.frame(self.locate(name, value))
# 切换default窗体
def switch_default(self):
self.driver.switch_to.default_content()
# 相对定位器
# def locator_with(self, method, value, el_name, el_value, direction):
# el = self.locate(el_name, el_value)
# direction_dict = {
# 'left': 'to_left_of', # 左侧
# 'right': 'to_right_of', # 右侧
# 'above': 'above', # 上方
# 'below': 'below', # 下方
# 'near': 'near' # 靠近
# }
# if isinstance(method, str):
# method_dict = {
# "id": By.ID,
# "xpath": By.XPATH,
# "link text": By.LINK_TEXT,
# "partial link text": By.PARTIAL_LINK_TEXT,
# "name": By.NAME,
# "tag name": By.TAG_NAME,
# "class name": By.CLASS_NAME,
# "css selector": By.CSS_SELECTOR
# }
# self.locate(locate_with(By.TAG_NAME, 'input').to_left_of(el))
# return self.driver.find_element(getattr(
# locate_with(method_dict.get(method), value), direction_dict.get(direction))(el))
# 句柄的切换(考虑不同场景的不同切换)
def switch_handle(self, close=False, index=1):
handles = self.driver.window_handles
if close:
self.driver.close()
self.driver.switch_to.window(handles[index])
# 断言文本信息:可以捕获异常进行处理,也可以不捕获,因为报错就相当于断言失败。
def assert_text(self, name, value, expect):
try:
reality = self.locate(name, value).text
assert expect == reality, '断言失败,实际结果为:{}'.format(reality)
return True
except Exception as e:
# self.log.exception('出现异常,断言失败:实际结果与预期不符合:{}'.format(e))
print('断言失败信息:' + str(e))
return False
# 获取指定元素的文本
def get_text(self, name, value):
return self.locate(name, value).text
人生苦短,及时行乐
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现