今天我们使用pytest来进行自动化测试的实战:
一.pytest基础操作
pytest规则:
1.pytest中的 测试用例(函数): 必须以 test_ 开头 或 _test结尾
2.pytest中的 测试模块文件(test_xxx.py): 必须以 test_开头
3.pytest中的 测试类 必须以 Testxxx开头
二.pytest运行方式
pytest.main([*args])
*args参数详解:
-s 显示输出语句的内容
-v 打印测试用例执行的状态
-m 标记名 执行指定标记名对应的测试用例
test_login.py 指定测试运行文件
三.pytest前置后置
类级别前置后置:
setup_class(cls)、teardown_class(cls)
函数级别前置后置:
setup(self)、teardown(self)
class TestDemo:
# 前置: 每条以test_开头的测试用例执行前, 执行一次
def setup(self):
print("setup")
# 后置: 每条以test_开头的测试用例执行后, 执行一次
def teardown(self):
print('teardown')
# 类级别前置: 第一条测试用例执行前, 执行一次, 仅一次
@classmethod
def setup_class(cls):
print("setup_class")
# 类级别后置, 最后一条测试用例执行后, 执行一次, 仅一次
@classmethod
def teardown_class(cls):
print("teardown_class")
四.pytest.ini配置文件
pytest.ini配置文件的优先级, 高于所有的pytest框架中的其它模块内容.
pytest.ini配置文件, 编码格式, 必须是 gbk.
[pytest]
opts == options 选项/参数
addopts = -s -v
指定测试用例类所在 模块文件的命名规则 必须满足 test_xxx.py
python_files = test_*.py
测试用例类的命名规则 Testxxx
python_classes = Test*
测试用例命名规则
python_functions = test_*
在执行pytest中的测试用例时, 测试用例函数在加载前会优先加载 pytest.ini中的规则.
markers标记: 过滤用例, 对用例进行分组执行.
markers =
smoke : smoking test
login : login test
demo : demo test
对测试用例进行标记:
@pytest.mark.标记名 # 标记名 必选先在 pytest.ini中的markers中进行描述, 才可以使用
执行时, 通过 参数 '-m 标记名' 进行过滤即可.
pytest.main(['-m 标记名'])
五.pytest参数化
@pytest.mark.parametrize("argsname",argsvalue)
argsvalue 必须是 列表[] 或 元组(); argsname是字符串参数数据; argsname必须作为形参出入到测试用例函数的形参位置
argsvalue 如果是嵌套结构, 当参数为1时, 将列表/元组中的元素, 循环赋值给字符串参数, 列表/元组中的元素有多少个, 则会循环多少次, 赋值了多少次.
当参数为2 或 多个时, 对于argsvalue的数据格式要求必须满足: 二维列表(嵌套列表) 或 列表嵌套元组, 元组嵌套列表.
反向测试数据
err_data = [
['错误的用户名','qftest02','qftest01.',{"code":1,"msg":"账号或密码不正确"}],
['错误的密码','qftest01','qftest01',{"code":1,"msg":"账号或密码不正确"}],
['用户名为空',None,'qftest01',{"code": 1, "msg": "参数不能为空"}],
['密码为空','qftest01',None,{"code": 1, "msg": "参数不能为空"}]
]
通过参数化的方式, 一个函数多次执行不同的 数据 实现相同函数内容的测试用例
参数化通过: @pytest.mark.parametrize(argsname, argsvalue)
argsname必须是字符串, argsname中的参数必须与argsvalue中的元素个数 保持一致
argsname会被pytest识别为 测试用例函数的 形参
parametrize() 建议使用列表或元组
@pytest.mark.parametrize("title,username,password,expect", err_data)
def test_login_errcase(self,title, username, password, expect):
result = login_check(username, password)
print(title)# 用例标题
assert result == expect
六.pytest断言- assert
assert a == b 断言a与b相等
assert a != b断言a与b不相等
assert a in b断言a包含在b中
assert a not in b断言a不包含在b中
assert a is None 断言a的值为None
assert a is not None 断言a的值不为None
案例:
test_login.py
from time import sleep
import pytest
import allure
from selenium import webdriver
err_datas = [
['错误的用户名','13800138007','123456','1111'],
['错误的密码','13800138006','1234567','1111'],
['用户名输入空字符串','','123456','1111'],
['密码输入空字符串','13800138006','','1111'],
['验证码输入空字符串','13800138006','123456',''],
['用户名超出长度','13800','123456','1111']
]
@allure.feature("登录功能")
class TestLogin:
def setup(self):
pass
def teardown(self):
sleep(1)
# 每条用例执行后, 刷新
self.driver.refresh()
@classmethod # 通过classmethod修饰的函数 级别是 类方法/类函数
def setup_class(cls):
cls.driver = webdriver.Chrome() # 创建Chrome类对象, 赋值给 类变量 cls.driver
cls.driver.maximize_window()
cls.driver.implicitly_wait(10)
cls.driver.get('http://10.36.176.114/index.php/Home/user/login.html')
@classmethod
def teardown_class(cls):
sleep(2)
cls.driver.quit()
@allure.story('反向')
@pytest.mark.err
@pytest.mark.parametrize('title,name,pwd,code', err_datas)
def test_login_err_case(self, title, name, pwd, code):
allure.dynamic.title(title)
self.driver.find_element_by_id("username").send_keys(name)
self.driver.find_element_by_id("password").send_keys(pwd)
self.driver.find_element_by_id("verify_code").send_keys(code)
self.driver.find_element_by_name('sbtbutton').click()
@allure.story("正向")
@pytest.mark.smoke
def test_login_success(self):
self.driver.find_element_by_id("username").send_keys('13800138006')
self.driver.find_element_by_id("password").send_keys('123456')
self.driver.find_element_by_id("verify_code").send_keys('111')
self.driver.find_element_by_name('sbtbutton').click()
runner.py
-- coding: utf-8 --
import pytest
import os
import shutil
if name == 'main':
# 1.判断 ./html ./report 目录是否存在
# 2. 如果存在, 则删除; 如果不存在, 则pass 略过
if os.path.exists('./html') and os.path.exists('./report'):
shutil.rmtree('./html')
shutil.rmtree('./report')
# --aluredir 目录 指定运行测试用例产生数据保存的目录
pytest.main(['--alluredir','./report'])
# allure generate 生成allure报告 从./report中加载数据 -o output 生成报告存储的位置
os.system('allure generate ./report -o ./html --clean') # --clean 清除之前的数据
最后:下面是配套学习资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!
软件测试面试小程序
被百万人刷爆的软件测试题库!!!谁用谁知道!!!全网最全面试刷题小程序,手机就可以刷题,地铁上公交上,卷起来!
涵盖以下这些面试题板块:
1、软件测试基础理论 ,2、web,app,接口功能测试 ,3、网络 ,4、数据库 ,5、linux
6、web,app,接口自动化 ,7、性能测试 ,8、编程基础,9、hr面试题 ,10、开放性测试题,11、安全测试,12、计算机基础