看了【 https://testerhome.com/topics/29298 】,没有看很懂
以及【 https://testerhome.com/topics/17292 】中使用好像是直接 scope 为 function,然后在参数中传入 driver
类似:
@pytest.fixture()
def driver():
driver = webdriver.Remote(remote_url, desired_caps)
yield driver
driver.quit()
def test_001(driver):
driver.find_element(By.ID, "xxx")
如果我想要用@pytest.fixture(scope="session", autouse=True)
, 但是这样的话,driver 如果要返回,只能在每个用例函数的参数中添加 driver,如上所示,第一,比较麻烦,第二,可能也会影响使用@pytest.mark.parametrize
,请问大家都是怎么初始化 driver 的呢?
另外,想要不写死数据,要用到参数化,如果放到 yml 文件中,数据就只能写死了,如果放到 py 文件中,数据可能是活的,但是可读性太差,这个问题有办法协调吗?
比如说 login,有密码错误和账号错误的情况,这种情况应该把账号写死吗,举个例子,手机号 13122226666,这个手机号是未注册的手机号,会提示错误,这个用例应该每次使用不同的错误的手机号吗,下次使用 15922229999 之类的。
我还是决定把这两个问题先解决。
针对这个需求,我想到了一个比较笨的想法,就是在 yaml 中使用 ${变量或者函数}的形式,但是我可以在 get yaml 中数据的时候,对里面符合上述形式的内容进行替换。代码如下:
def get_yaml(filepath, field):
"""
读取yaml文件
:param filepath: 文件路径
:param field: yaml中的key
:return: dict,list
"""
with open(filepath, 'r', encoding='utf-8') as f:
file_data = f.read()
data = yaml.load(file_data, Loader=yaml.FullLoader)
f.close()
raw_data = data[field]
# 将原生数据转为字符串类型
string = str(raw_data)
# 从字符串中搜索符合${函数/变量}的字符串,并将其中的函数/变量计算出来
for expression in re.findall("(?<=\\${)[A-Za-z_][A-Za-z0-9_()^{}]+(?=})", string):
value = str(eval(expression))
# 将其代替其中的函数/变量
string = string.replace(f"${{{expression}}}", value)
return eval(string)
比较笨,而且里面的正则表达式不知道有没有囊括所有情况,但是算是一种办法。
↑
想要不写死数据,要用到参数化,如果放到 yml 文件中,数据就只能写死了,如果放到 py 文件中,数据可能是活的,但是可读性太差,这个问题有办法协调吗?
就像 https://testerhome.com/topics/29298
一楼提到的,直接获取 driver 时使用单例不行吗?
他们也在说这个不是特别好的办法,我之前是这么做的,既然@pytest.fixture(scope="session", autouse=True)
存在,我觉得应该也是可以用的。
个人感觉单例挺好的,哈哈哈,看到别人有这么做的,在 fixture 里把 driver 设置成 global (我没试过是否可以 )
@pytest.fixture(scope='session', autouse=True)
def drivers(request):
global driver
if driver is None:
driver = webdriver.Chrome()
driver.maximize_window()
def fn():
driver.quit()
request.addfinalizer(fn)
return driver
放在 yaml 中的数据好像可以不是写死的。。类似 key:$.data 的方式,不过我不清楚别人是如何实现的。。
我曾在社区看到过一位大佬在 yaml 中用过 key:$.data,也在别的地方看到过一次,就是 python 的,具体实现就不太清楚了。。
yaml 中:
读取后输出:
好像是无法使用的。
我还是决定把这两个问题先解决。
针对这个需求,我想到了一个比较笨的想法,就是在 yaml 中使用 ${变量或者函数}的形式,但是我可以在 get yaml 中数据的时候,对里面符合上述形式的内容进行替换。代码如下:
def get_yaml(filepath, field):
"""
读取yaml文件
:param filepath: 文件路径
:param field: yaml中的key
:return: dict,list
"""
with open(filepath, 'r', encoding='utf-8') as f:
file_data = f.read()
data = yaml.load(file_data, Loader=yaml.FullLoader)
f.close()
raw_data = data[field]
# 将原生数据转为字符串类型
string = str(raw_data)
# 从字符串中搜索符合${函数/变量}的字符串,并将其中的函数/变量计算出来
for expression in re.findall("(?<=\\${)[A-Za-z_][A-Za-z0-9_()^{}]+(?=})", string):
value = str(eval(expression))
# 将其代替其中的函数/变量
string = string.replace(f"${{{expression}}}", value)
return eval(string)
比较笨,而且里面的正则表达式不知道有没有囊括所有情况,但是算是一种办法。
但是还是有缺陷的,比如 yaml 中的函数必须和 get_yaml 函数在同一个 py 文件中,不然计算不出来。但是还是放到这里,期望有人可以提出其他的想法吧。
driver 问题有两种比较优雅的方式。
方式一,autouse,参考代码:
class TestClass:
@pytest.fixture(autouse=True)
def transact(self, request, db):
db.begin(request.function.__name__)
yield
db.rollback()
def test_method1(self, db):
assert db.intransaction == ["test_method1"]
def test_method2(self, db):
assert db.intransaction == ["test_method2"]
方式二,conftest,参考代码:
这篇文章件介绍了 yaml 文件如何参数化
https://www.cnblogs.com/yoyoketang/p/14119435.html
我的是自己封装了 driver 类 然后用了单例装饰器 这样整个脚本运行期间用的都是同一个 session。另外 参数化用的是 excel 读取 excel 的数据转换成列表,用@pytest.mark.parametrize 直接搞定参数化
谢谢,确实解决了变量的问题,但是好像不能解析函数。
比如说,账号错误,密码错误的情况,每次测试的数据应该是一样的呢,还是应该不一样?如果是一样的,那可以写死,如果不一样,可能就要用到 faker 库,random 库之类的,并且用到里面的函数,比如像:
account: ${random.randint(2, 49)}
这种的,请问应该怎么解决?
yanl 文件应该不能放函数,可以在替换的时候使用函数:
account: $account
name: $name
from string import Template
import yaml
import random
from faker import Faker
fake = Faker(locale='zh_CN')
with open("login.yaml", encoding='utf-8') as fp:
read_yml_str = fp.read()
tempTemplate1 = Template(read_yml_str)
c = tempTemplate1.safe_substitute({"account": random.randint(2, 49), "name": fake.name()})
print(c)
yaml_data = yaml.safe_load(c)
print(yaml_data)
确实可以这样做,想问一下用例的错误情况,比如说手机号码的格式,10 位数,11 位数但是是 3 开头,12 位数,没有通过认证的手机号码,这种都是不符合的情况,会出现提示信息,这算是一种用例,我想知道的是,这几种情况需要每次输入的错误数据相同吗?如果需要每次输入的错误数据不同,那么 tempTemplate1.safe_substitute({"account": random.randint(2, 49)}) 中的函数便会太多太多了,有 random_phone(length=10),random_phone(length=12) 等等。
没太懂你的意思,是要在 yaml 文件使用参数么?
我是这样做的:
yaml:
解析 yaml 文件,替换 ${}
def _replace_method(self,filename="",**kwargs):
data = Yaml_read.yaml_read(filename)
data1 = json.dumps(data)
for key,value in kwargs.items():
data1 = data1.replace(f'${{{key}}}',value)
data2 = json.loads(data1)
return data2
这样可以通过传参的方式动态替换 yaml 文件的变量