UI自动化测试重点思考--装饰器
- 装饰器
- 装饰器定义
- 装饰器代码示例
- 装饰器的执行
- 如何将装饰器融合到pytest框架里面
- 生成器
- 创建生成器
- 生成器的定义
- 如何将生成器融合到pytest框架里面
- fixture(夹具)使用
- pytest fixture 中 scope 参数的详细解释
- 描述符的总结
- 描述符的定义
- 描述符的作用
- ddt数据驱动
- 简单示例
- 升级示例
- 多线程
- 安装pytest-xdist 插件
- 采用多线程编程
装饰器
装饰器定义
装饰器是一个函数A,函数A的传参是函数B,且函数A内部还有一个函数C,且函数A返回一个函数C,函数C是包含函数B的。
装饰器代码示例
def my_decorator(func):
def wrapper():
print("Something is happening before the function is called.")
func()
print("Something is happening after the function is called.")
return wrapper
@my_decorator
def say_hello():
print("Hello!")
say_hello()
在这个例子中,my_decorator 是一个装饰器函数,它接受一个函数作为参数。wrapper 函数是由装饰器内部定义的一个新函数,它包裹了原始的 say_hello 函数,并添加了额外的功能。当调用 say_hello() 时,实际上是调用了 wrapper 函数,从而触发了装饰器中定义的操作。
装饰器的执行
- 当 Python 解释器执行到
@my_decorator
时,它会立即将被装饰的函数say_hello
传递给my_decorator
函数,相当于执行了say_hello = my_decorator(say_hello)
。 - 在 my_decorator 函数内部,它定义了一个内部函数
wrapper
,该函数包含了在被装饰函数调用前后需要执行的逻辑。 - 当调用
say_hello()
时,实际上是调用了wrapper()
函数,因为 say_hello 已经被重新指向了 wrapper 函数。 - 在 wrapper 函数内部,首先会执行装饰器定义的
前置逻辑
,然后调用被装饰的函数 func()
,即调用原始的 say_hello 函数。 - 在调用完被装饰函数后,继续执行 wrapper 函数中的
后置逻辑
。
如何将装饰器融合到pytest框架里面
1.全局管理 driver,运行测试用例的时候先调用 driver 传入测试用例中,测试用例就可以使用 driver来操作浏览器了。
2.实现执行测试用例之前先登录或者先打开网址,执行测试用例之后再自动登出的操作。
生成器
创建生成器
- 生成器推导式
gen = (x ** 2 for x in range(1, 6))
- 含有yield关键字
def squares(n):
for i in range(1, n + 1):
yield i ** 2
print("这里被挤出了一个糖")
gen=squares(5)
定义了一个名为 squares 的函数,该函数接受一个整数 n 作为参数。在函数内部,使用 yield 语句来生成从 1 到 n 的整数的平方值,并在最后打印一条信息 “这里被挤出了一个糖”。
生成器的定义
1.只要是实现了yield关键字的函数都是一个生成器
2.生成器的特点是,当调用者执行到yield就会卡住,然后把yield后面的参数返回给调用者
3.当接收到next()函数的命令的时候才会执行yield关键字下面的代码
如何将生成器融合到pytest框架里面
1.自动登录登出中要使用生成器
实现了执行测试用例之前先执行前置操作【登录】【因为有装饰器】直到代码运行到有 yield 关键字的时候才会卡住,当执行完测试用例之后,pytest 自动调用 next 函数,才会去执行 yield 关键字后的后置操作【登出】。
fixture(夹具)使用
pytest fixture 中 scope 参数的详细解释
Scope 参数值 | 描述 |
---|---|
function | 每个测试函数都会调用一次 fixture 函数,并且在测试函数执行完毕后立即销毁。 |
class | 在测试类中的所有测试方法之间共享 fixture,fixture 在第一个测试方法运行前创建,在最后一个测试方法运行后销毁。 |
module | 在整个测试模块中共享 fixture,fixture 在第一个测试函数运行前创建,在最后一个测试函数运行后销毁。 |
session | 在整个 pytest 会话期间共享 fixture,fixture 在 pytest 启动时创建,在 pytest 结束时销毁。 |
描述符的总结
描述符的定义
描述符是实现了特定协议的类,它至少包含 get()、set() 和 delete() 中的一个方法。
描述符的作用
描述符可以用于控制对类的属性的访问、修改和删除,允许你在属性被访问、修改或删除时执行自定义的逻辑。
class PageElement:
def __get__(self, instance, owner):
print("Getting the value")
return instance.__dict__.get(self.attr_name, None)
def __set__(self, instance, value):
print("Setting the value")
instance.__dict__[self.attr_name] = value
def __delete__(self, instance):
print("Deleting the value")
del instance.__dict__[self.attr_name]
class Page:
element = PageElement()
def __init__(self):
self.element = None
@property
def element(self):
return self._element
@element.setter
def element(self, value):
self._element = value
# 示例用法
page = Page()
page.element = "Hello" # 设置属性值,调用 PageElement.__set__()
print(page.element) # 获取属性值,调用 PageElement.__get__()
del page.element # 删除属性值,调用 PageElement.__delete__()
ddt数据驱动
简单示例
import pytest
import time
@pytest.mark.smoke
@pytest.mark.parametrize('username, password', [
('user1', 'DasSWOLd'),
('user2', 'pass2'),
('wzz', '12345')
])
def test_001(self, drivers, username, password):
zhufeng = ZhuifengIndexPage(drivers)
zhufeng.input_account(username)
zhufeng.input_password(password)
zhufeng.click_login_button()
time.sleep(3)
- @pytest.mark.smoke:是一个 pytest 的标记,用于标记测试用例为 smoke 测试,表示这是一个简单的冒烟测试,主要验证系统的基本功能。
- @pytest.mark.parametrize(‘username, password’, […]):是 pytest 提供的一个装饰器,用于为测试用例提供多组参数数据。
- 测试用例 test_001(self, drivers, username, password):这是一个测试方法,其参数包括 drivers、usernausername 和 password 是测试数据,由 @pytest.mark.parametrize 提供。me 和 password。
升级示例
def read_csv_file(file_path):
"""生成器方式去读取csv里面的数据来做数据驱动测试,yield关键字来控制一行一行的读取字典里面的内容(字典里面的数据是隐形的,还未产生,就和奶糖盒子一样的道理)"""
with open(file_path, 'r', newline='') as file:
reader = csv.DictReader(file) # 这是一个迭代器对象,把每次读取出来的数据都放到字典里面存起来,下面用一个for循环一次一次的去读取字典里面的数据,确保不会一次性将所有的数据读取到内存中。
for row in reader: # 如过下面没有生成器,那么这里直接全部数据都遍历一遍,如果有生成器就会卡住,一个一个来,接收到next方法才会读取下一行。
yield row['username'], row['password']
@pytest.mark.smoke
@pytest.mark.parametrize('username, password', read_csv_file(r'data/data.csv'))
def test_001(drivers, username, password):
zhufeng = zhuifeng_index_page(drivers)
zhufeng.input_account = username
zhufeng.input_password = password
# zhufeng.log_in_button.click()
zhufeng.click_log_in_button
# assert drivers.current_url == 'https://exam.wzzz.fun'
1.read_csv_file 函数:
- 这是一个生成器函数,用于逐行读取 CSV 文件中的数据。使用 yield
关键字,每次从文件中读取一行数据
,并返回一个包含用户名和密码的元组。
- @pytest.mark.parametrize(‘username, password’, read_csv_file(r’data/data.csv’)):
- 使用 @pytest.mark.parametrize 装饰器标记测试用例,并提供了多组用户名和密码的数据源。
- 数据源来自于 read_csv_file 函数返回的生成器,通过调用 read_csv_file 函数来读取 CSV文件中的数据,并将其作为参数传递给测试用例。
3.def test_001(drivers, username, password): - 在测试方法中,首先创建了一个 zhufeng_index_page 对象,然后输入了用户名和密码,并点击了登录按钮(注释掉的部分)
- 这个测试方法会被 @pytest.mark.parametrize 多次调用,每次调用时传入不同的用户名和密码数据。
多线程
安装pytest-xdist 插件
pip install pytest-xdist
采用多线程编程
1、可以使用 threading 线程来指定要并发执行的测试用例,但是用例数量一旦多了,就不好去人工分配哪一个线程执行那部分用例,这时候可以使用插件 pytest-xdist,执行 pytest-n5就好了,使用多少个线程并发执行还是得看电脑性能如何,一般使用10~20 个线程为最佳,在自己电脑上就使用5个线程就好了。
2、多线程并发执行用例,相当于多个人同时进行测试,宏观上是这样的,不过微观上对于 cpu 都是串行的,只是调度每一个线程的速度非常快,看起来是并发的而己,因为只有一个cpu,同一时刻,只能执行一个线程,如果有多个cpu 才能真正意义上实现多个测试用例并发执行,不过对于测试而言,无伤大雅,我们了解原理即可,我们主要重在使用。