前言:
在单元测试框架中,主要分为:测试固件,测试用例,测试套件,测试执行及测试报告;
测试固件不难理解,也就是我们在执行测试用例前需要做的动作和测试执行后的需要做的事情;
比如在UI自动化测试中,我们更加关注的是界面的操作,而不是关注打开浏览器和关闭浏览器;
在数据库中,我们更加关注的是怎么去操作Mysql数据库,而不是关注数据库的连接和断开;
所以如上的这些打开浏览器和关闭浏览器,连接数据库和断开数据库,我们可以让测试固件去干,测试用例层面是关住执行结果和断言结果,
如:UnitTest单元测试框架中的setup和teardown,还有setupClass和teardownClass(需要配合@classmethod装饰器一起使用)是测试固件;
那么本节是介绍pytest的类似于这类的固件;
1、FixTure返回值,代码示例如下:
'''
fixture的作用
1. return 返回值
2. 初始化和清理操作
3. conftest.ini 全局
'''
import json
import pytest
import requests
import yaml
# 读取BookYaml文件
def readYaml():
with open('books.yaml','r',encoding='utf-8') as fp:
return list(yaml.load_all(fp))
@pytest.fixture()
# 认证登录
def get_Token():
login_url = "http://127.0.0.1:5550/auth"
data = {"username": "admin","password": "Admin"}
response = requests.post(url=login_url,json=data)
# 返回token值
return response.json()['access_token']
# 使用Pytest参数化思想
@pytest.mark.parametrize('datas',readYaml())
# 定义测试方法
def test_books(get_Token,datas):
if datas['method'] == 'get':
r = requests.get(
url=datas['url'],
headers= {"Authorization":"JWT {0}".format(get_Token)}
)
# 断言Get方法返回的结果
assert datas['expect'] in json.dumps(r.json(),ensure_ascii=False)
elif datas['method'] == 'post':
r = requests.post(
url=datas['url'],
json=datas['dict1'],
headers= {"Authorization":"JWT {0}".format(get_Token)}
)
# 断言Post方法返回的结果
assert datas['expect'] in json.dumps(r.json(),ensure_ascii=False)
elif datas['method'] == 'put':
r = requests.put(
url=datas['url'],
json=datas['dict1'],
headers= {"Authorization":"JWT {0}".format(get_Token)}
)
# 断言Put方法返回的结果
assert datas['expect'] in json.dumps(r.json(),ensure_ascii=False)
elif datas['method'] == 'delete':
r = requests.delete(
url=datas['url'],
headers= {"Authorization":"JWT {0}".format(get_Token)}
)
# 断言Delete方法返回的结果
assert datas['expect'] in json.dumps(r.json(), ensure_ascii=False)
if __name__ == '__main__':
# 执行模块
pytest.main(['-s','-v','test_fixture_api.py'])
装饰器@pytest.fixture(),它声明是一个函数fixture,如果测试函数的参数列表中包含了fixture名,那么执行pytest时会检测到它,且在测试函数执行前执行fixture,再把返回数据给测试函数
接下来看下另外的案例,测试UI自动化场景时,不管怎么都得打开浏览器进行操作,最后再关闭浏览器,此时用到fixture来处理
# UI案例示例
import pytest
from selenium.webdriver.common.by import By
# 安装pip3 install pytest-selenium
@pytest.fixture()
def int(selenium):
selenium.maximize_window()
selenium.implicitly_wait(10)
selenium.get('http://www.baidu.com/')
yield
selenium.quite()
def test_baidu_title(int,selenium):
assert selenium.title == '百度一下,你就知道'
def test_so_text(int,selenium):
so = selenium.find_element(By.ID,'kw')
so.send_keys('pytest')
assert so.get_attribute('value') == 'pytest'
def test_url(int,selenium):
selenium.find_element_by_link_text('新闻').click()
# 获取全部的handles再进行切换到指定的窗口
selenium.switch_to.window(selenium.window_handles[0])
assert selenium.current_url == 'http://news.baidu.com/'
if __name__ == '__main__':
pytest.main()
# 在终端上输入如下命名执行:pytest -v 测试模块 --driver Chrome(指定测试的浏览器)
# 命令:pytest -v -s test_fixture_ui.py --driver Firefox
2、fixture之conftest.py
2.1 通过conftest.py可以共享fixture,是pytest特有的本地测试配置文件,既可以用来设置项目级别的fixture,可以用来导入外部插件,还可用来指定钩子函数(共享)
2.2 conftest.py 文件是不可以导入的,只可共享它所在目录及子目录
Mysql案例,把链接数据库方法放在conftest.py 模块下
import pymysql
import pytest
# 连接数据库
@pytest.fixture()
def connSql():
conn = pymysql.connect(
host='localhost',
user='root',
password='123456',
charset='utf8',
database='srs'
)
return conn
Mysql代码如下:
import pytest
# 关闭数据库
def closeSql(connSql):
connSql.close()
# 创建游标
@pytest.fixture()
def connCursor(connSql):
return connSql.cursor()
# 关闭游标
def closeCursor(connSql):
connSql.close()
@pytest.fixture()
def int(connSql,connCursor):
connSql
connCursor
yield
closeSql(connSql)
connCursor(connSql)
# 查询数据库
def test_select_sql(connSql,connCursor):
# 创建游标
sql = 'select * from tbcourse'
# 执行游标
connCursor.execute(sql)
result = connCursor.fetchall()
print('查询数据: ',result)
if __name__ == '__main__':
pytest.main(['-v','-s','test_fixture_sql.py'])
执行结果如下:
终端中运行:pytest -s -v test_fixture_sql.py
使用conftest.py有这个几点需要特别的注意:
1、conftest.py文件绝对不能当一般的模块导入,这点需要特别的注意
2、conftest.py很多时候被当作pytest测试框架的一个本地插件库来执
3、在一个测试包(目录)下面,conftest.py被看成是该目录下所有测试用例使用的fixture仓库
conftest.py 文件中主要是依照scope的参数来控制fixture执行配置和销毁逻辑,下面介绍的是fixture默认值 function函数
分为四类分别为:
Session:会话级别:针对一个项⽬中所有的模块,类,以及测试函数
Module:模块级别,主要是针对一个模块的范围
Class:针对类级别,主要是针对一个类的范围
Function:函数级别,主要是针对一个函数的
fixture级别之:function函数级别
import pytest
from selenium import webdriver
@pytest.fixture(scope='function')
def driver():
return webdriver.Firefox()
@pytest.fixture(scope='function')
def init(driver):
driver.maximize_window()
driver.get('http://www.baidu.com/')
# 隐性等待
driver.implicitly_wait(10)
yield
driver.quit()
测试模块代码为:
import pytest
from selenium.webdriver.common.by import By
def test_baidu_title(driver,init):
assert driver.title == '百度一下,你就知道'
def test_so_text(int,driver):
so = driver.find_element(By.ID,'kw')
so.send_keys('pytest')
assert so.get_attribute('value') == 'pytest'
def test_url(int,driver):
driver.find_element_by_link_text('新闻').click()
# 获取全部的handles再进行切换到指定的窗口
driver.switch_to.window(driver.window_handles[0])
assert driver.current_url == 'http://news.baidu.com/'
if __name__ == '__main__':
pytest.main()
fixture级别之:class、module,session函数级别
import pytest
from selenium import webdriver
# fixture级别之class
@pytest.fixture(scope='class')
def driver():
return webdriver.Firefox()
@pytest.fixture(scope='class')
def init(driver):
driver.maximize_window()
driver.get('http://www.baidu.com/')
# 隐性等待
driver.implicitly_wait(10)
yield
driver.quit()
# fixture级别之module
@pytest.fixture(scope='module')
def driver():
return webdriver.Firefox()
@pytest.fixture(scope='module')
def init(driver):
driver.maximize_window()
driver.get('http://www.baidu.com/')
# 隐性等待
driver.implicitly_wait(10)
yield
driver.quit()
# fixture级别之session
@pytest.fixture(scope='session')
def driver():
return webdriver.Firefox()
@pytest.fixture(scope='session')
def init(driver):
driver.maximize_window()
driver.get('http://www.baidu.com/')
# 隐性等待
driver.implicitly_wait(10)
yield
driver.quit()
测试模块代码为:
import pytest
from selenium.webdriver.common.by import By
class TestUi(object):
def test_baidu_title(self,int,selenium):
assert selenium.title == '百度一下,你就知道'
def test_so_text(self,int,selenium):
so = selenium.find_element(By.ID,'kw')
so.send_keys('pytest')
assert so.get_attribute('value') == 'pytest'
def test_url(self,int,selenium):
selenium.find_element_by_link_text('新闻').click()
# 获取全部的handles再进行切换到指定的窗口
selenium.switch_to.window(selenium.window_handles[0])
assert selenium.current_url == 'http://news.baidu.com/'
if __name__ == '__main__':
pytest.main()