测试工程师社招-pytest框架
(关于测试框架,面试问的不多,比如:项目用到多少,跑出来多少问题等,不会详细问怎么写)
基础知识
一、Python+request测试接口案例
1、测试get接口
import requests
r = requests.get('https://api.github.com/events')
print(r.status_code)
print(r.json())
2、测试get param接口
import requests
params={
"shouji":"***",
"appkey":"***"
}
r = requests.get(url = 'http://api.binstd.com/shouji/query',params=params)
print(r.json())
3、测试post json接口
import requests
json_data = {
"title":"foo",
"body":"bar",
"userId":1
}
r = requests.post(url = "https://jsonplaceholder.typicode.com/posts",json = json_data)
print(r.status_code)
print(r.json())
4、测试post param接口
import requests
params ={
"shouji":"***",
"appkey":"***"
}
r = requests.post(url = "http://api.binstd.com/shouji/query",params = params)
print(r.status_code)
print(r.json())
5、测试post formdata接口
import requests
data = {
"text":"hello"
}
r = requests.post(url = "https://dict.youdao.com/keyword/key",data = data)
print(r.json())
二、pytest框架
Pytest是什么:
单元测试框架,在自动化测试或者白盒测试中针对软件的最小单元(函数、方法)进行测试
主要做什么:
发现测试用例、执行测试用例、判断测试结果、生成测试报告
插件:
pytest
pytest-html(生成html报告的插件)
pytest-xdist(多线程运行的插件)
pytest-ordering(改变用例的执行顺序的插件)
pytest-rerunfailres(失败用例重跑的插件)
allure-pytest(生成美观自定义的allure报告)
(pip insatll -r requirements.txt(去运行安装所有的插件))
命名规则:
1、模块名必须以test_开头或者以_test结尾2、测试类必须以test开头,并且不能带有init方法3、测试用例必须以test_开头
Testcase Test test
如何执行:
1、命令行的方式执行pytest
-v 输出详细信息
-s 输出调试信息 -vs
-n 多线程运行(pytest -xdist) pytest -vs -n =2
(一个线程里面有多个进程,6秒,两个线程运行5秒--每个线程需要创建时间)
-reruns num失败重跑(插件:rerunfailres) pytest -vs --reruns=2
Raise Exception(“”)抛出异常 try except解决异常
-x 出现一个用例失败则停止测试 pytest -vs -x
-maxfail 出现几个失败才终止
-html生成html的测试报告(插件:pytest -html) pytest -vs --html ./reports/result.html
-k运行测试用例名称中包含某个字符串的测试用例 pytest -vs -k “baili or xingyao”
2、通过main方式执行
if __name__ == '__main__':
pytest.main(["-vs"])
3、通过全局配置文件pytest.ini文件执行,一般放在项目的根目录下1、可以改变默认的测试用例规则2、主函数和命令行都可以加载3、测试用例分组执行4、 -m “smoke’只执行冒烟用例 @pytest.mark.smoke
[pytest] addopts = -vs --alluredir=./temps --clean-alluredir testpaths = ./testcase python_files = test_*.py python_classes = Test* python_functions = test_*
pytest如何跳过测试用例:()
1、无理由跳过 2、有理由跳过
import pytest
@pytest.mark.skip(reason = "无理由跳过")
def test01():
print("测试")
@pytest.mark.skipif(12>10,reason = "有理由跳过")
def test02():
print("测试01")
def test03():
print("测试03")
if __name__ == '__main__':
pytest.main(['-vs'])
Pytest测试用例的前后置,setup和teardown,不同等级,封装在一个公共包、公共类里面函数级别、类级别
模块级:setup_module/teardown_module
函数级:setup_function/teardown_function
类级:setup_class/teardown_class
类中的方法级:setup_method/teardown_method
使用fixture实现部分前后置(共享初始化、清理代码)
1、scope:作用域 @pytest.fixture(scope = “function”,autouse = True)
Function:在函数之前或者之后执行(可以手动调用,也可以自动执行),有return或者yield返回值,可以把这个值传递到测试用例当中,yield后置
Return和yield区别:1、返回某个值,返回后函数不在继续执行彻底结束2、yield生成器 返回结果后函数不结束 暂定 再次调用时暂定的地方继续执行,可执行多次
@pytest.fixture(scope = "function",autouse=True)
def func():
print("我是前置步骤")
yield
print("我是后置步骤")
Class:在类之前和类之后执行,自动执行或者手动调用(@pytest.mark.usefixtures(“exe_sql”)) @pytest.mark.usefixtures(“”)
Package、session:在整个项目会话之前和之后执行,需要结合contest.py文件使用
2、autouse:自动执行,默认是False
3、Params:实现参数化,如何把值传到fixture,通过fixture函数的参数里面加入request来接收这个参数,可以通过request.param取值
@pytest.fixture(scope ="function",autouse=False,params=read_yaml())
def exe_database_sql(request):
print(request.param)
print("执行sql查询")
yield
print("关闭数据库连接")
4、ids:不能单独使用,必须和params一起使用,作用是对参数起别名
@pytest.fixture(scope ="function",autouse=False,params=read_yaml(),ids=['z','m'])
5、name:给fixture起别名,一旦使用,只能用别名
Fixtrue结合conftest.py文件使用:
1、conftest.py专门用来存放fixtrue的配置文件
2、在conftest.py文件里面所有的方法在调用时都不需要导包
3、Conftest.py文件可以有多个,并且conftest.py文件里面的多个fixtrue可以被同一个用例调用
优先级:
fixture的session
fixture的class
Setup_class
fixture的function
Setup
pytest执行过程:
1、查找当前目录下的conftest.py文件
2、查找当前目录下的pytest.ini文件(找测试用例的位置)
3、查找用例目录下的conftest.py
4、查找py文件中是否有setup、teardown、setup_class、tear_class
5、在根据pytest.ini文件中的测试用例规则去查找用例并执行
Pytest断言:assert
Allure-pytest插件生成更美观的报告
1、安装插件
2、下载allure,解压,解压后配置环境变量
3、验证allure https://www.cnblogs.com/lirongyu-test/p/16656478.html
4、生成allure报告
a、生成临时的json报告,在pytest.ini文件里面加入以下内容
addopts = -vs --alluredir=./temps --clean-alluredir
b、生成更美观的allure报告
c、定制化
Pytest之parametrize()实现数据驱动,@pytest.mark.parametrize(args_name,args_value),args_name:参数名称,用于将参数的值传递给函数;args_value:参数值(列表和字典列表,元祖和字典元祖),有n个值用例执行n次
#单参数单次循环
@pytest.mark.parametrize("name",['老白'])
def test_parametrize01(name):
print("测试参数化"+name)
#单参数多次循环,运行时将数组里的值分别赋值给变量,每赋值一次,运行一次
@pytest.mark.parametrize("name",['老白','老黑'])
def test_parametrize02(name):
print("测试参数化" + name)
#可以数组\可以元祖
@pytest.mark.parametrize("name,word",[['老白','123'],['老黑','456']])
def test_parametrize03(name,word):
print(f'{name}的台词是{word}')
Yaml格式测试用例读写封装
1、yaml是一种数据格式,扩展名可以使yaml.xml,#注释 ,缩进表示层级关系,区分大小写
yaml读取出来是一个字典列表 每加一次就多一个用例
2、用于做配置文件(yaml.ini);用于编写自动化测试用例
-
name: 获取统一的鉴权码
request:
method: get
url: https://api.weixin.qq.com/cgi-bin/token
data:
grant_type: client_credential
appid: ***
secret: ***
validate: None
实战项目分享
一、requests使用
1)testcase:定义测试用例 1、导包 2、定义登录方法3、定义测试数据 4、发送post请求 5、输出结果
#1、导包
import requests
#2、定义登录方法
def login():
#3、定义测试数据
url = 'https://api.weixin.qq.com/cgi-bin/token'
params = {
"grant_type":"client_credential",
"appid":"wx71385b4e0a37259d",
"secret":"8963b6138d123eb0edf9fb5d8f453c8f"
}
#4、发送post、get请求
r = requests.get(url=url,params = params)
#5、输出结果
print(r.json())
2)utils:封装公共方法
封装get和post方法:1、创建封装get和post的方法 2、发送requests get或post请求 3、获取结果相应内容 4、内容存到相应字典 5、字典返回
#单独封装get请求
def request_get(url,params):
r = requests.get(url,params = params)
code = r.status_code
try:
body = r.json()
except Exception as e:
body = r.text
res = dict()
res['code'] = code
res['body'] = body
return res
#get方法调用
r = request_get(url,params = params)
print(r)
#单独封装post请求
def request_post(url,params,json):
r = requests.post(url,params = params,json = json)
code = r.status_code
try:
body = r.json()
except Exception as e:
body = r.text
res = dict()
res["code"] = code
res['body'] = body
return res
#post方法调用
r = request_post(url,params=params,json = json)
print(r)
get和post方法重构:1、定义一个类 2、定义判断方法:如果是get发送get方法,如果是post发送post方法 获取相应的code body 3、重构get post方法 4、在测试用例里面调用 初始化类 调用相应的get post方法
#重构:1、创建类 2、定义公共方法 3、重构get和post方法
class Request:
def request_api(self,url,data = None,params = None,json = None,cookies = None,method = "get"):
#1、增加方法的参数,根据参数来验证方法get / post请求,方法请求
if method == "get":
r = requests.get(url, params=params, json=json,cookies = cookies)
elif method == "post":
r = requests.post(url, params=params, json=json,cookies = cookies)
# 2、重复的内容,复制进来
code = r.status_code
try:
body = r.json()
except Exception as e:
body = r.text
res = dict()
res['code'] = code
res['body'] = body
return res
#get、post方法重构:1、定义方法 2、定义参数(可变参数) 3、调用公共方法
def get(self,url,**kwargs):
#url method json headers cookies
return self.request_api(url,method = "get",**kwargs)
def post(self,url,**kwargs):
return self.request_api(url,method = "post",**kwargs)
#调用:1、初始化类 2、接收get post方法 3、打印信息
request = Request()
r = request.post(url,params = params,json = json)
print(r)
二、yaml配置文件
yaml文件格式:字典和列表
字典:1、字典里的键值对“:”分隔 2、字典直接写key和value,每一个键值对占一行 3、后面要有空格
name: "test_yaml"
result: "sucess"
列表:1、一组按序排列的值(简称序列或列表)2、数组前加有“-”符号,符号与值之间需要用空格分隔
#["a","b","c"]
- "a"
- "b"
- "c"
1、字典嵌套字典
#{person1:{"name":"xiaoming","age":"18"},person2:{"name":"xiaohong","age":"20"}}
person1:
name: xiaoming
age: 18
person2:
name: xiaohong
age: 20
2、字典嵌套列表
#{person:["a","b","c"]}
person:
- "a"
- "b"
- "c"
3、列表嵌套列表
-
- "a"
- "b"
- "c"
-
- "1"
- "2"
- "3"
4、列表嵌套字典
#[{"username1":"test1",{"password1":"111","username2":"test2"}}]
- username1: "test1"
- password1: "111"
username2: "test2"
读取文件:1、读取单个文件2、读取多个文件
# 1、创建yaml格式文件
# 2、读取这个文件 (1、导入yaml包 2、打开文件 3、使用yaml读取文件) 字典格式 :注意空格
# 3、输出这个文件
#读取单个文档
import yaml
with open("./data.yml","r",encoding="utf-8") as f:
r = yaml.safe_load(f)
print(r)
#读取多个文档
---
"用户名称1": "text123"
"密码": "123456"
---
"用户名称1": "text456"
"密码": "123456"
#1、编辑或修改data.yml 2、yaml 读物方法,all 3、循环打印
import yaml
with open("./data.yml","r",encoding="utf-8") as f:
r = yaml.safe_load_all(f)
for i in r:
print(i)
utils包-创建yamlReader类,初始化类读取yaml文件:
1、创建一个YamlReader类 2、初始化,判断文件是否存在 os.path.existd() 不存在就抛出异常 初始化文件的内容dat 3、读取单个文件 没数据返回初始化数据 有数据返回读取到的数据 4、读取多个文件 没数据返回初始化数据 有数据返回读取到的数据 (list接收)
#1、创建类 2、初始化,文件是否存在 3、yaml文件读取
import os
import yaml
class YamlReader:
#初始化,判断文件是否存在
def __init__(self,yamlIf):
if os.path.exists(yamlIf):
self.yamlIf = yamlIf
else:
raise FileNotFoundError("文件不存在")
self._data = None
self._data_all = None
#yaml读取
#单个文档读取
def data(self):
#第一次调用data,读取yaml文档,如果不是,直接返回之前保存的数据
if not self._data:
with open(self.yamlIf,"rb") as f:
self._data = yaml.safe_load(f)
return self._data
#多个文档读取
def data_all(self):
#第一次调用data,读取yaml文档,如果不是,直接返回之前保存的数据
if not self._data_all:
with open(self.yamlIf,"rb") as f:
self._data_all = list(yaml.safe_load_all(f))
return self._data_all
#yaml_demo.py文件中调用
from utils.YamlUtil import YamlReader
# res = YamlReader("./data.yml").data()
res = YamlReader("./data.yml").data_all()
print(res)
config包:1、创建yaml文件:填写url相关信息 2、创建conf.py文件1)获取conf.yaml的目录2读取conf.yaml文件
import os
from utils.YamlUtil import YamlReader
#1、获取项目基本目录
#获取当前项目的绝对路径
current = os.path.abspath(__file__)
# print(current)
BASE_DIR = os.path.dirname(os.path.dirname(current) ) #获取父级目录,获取两次
#定义config目录的路径
_config_path = BASE_DIR + os.sep + "config"
#定义conf.yml文件的路径
_config_file = _config_path + os.sep +"conf.yaml"
#变量是私有的,定义一个方法访问
def get_config_path():
return _config_path
def get_config_file():
return _config_file
#2、读取配置文件
#创建类
class ConfigYaml:
#初始yaml读取配置文件
def __init__(self):
self.config = YamlReader(get_config_file()).data()
#定义方法获取需要的信息
def get_conf_url(self):
return self.config["BASE"]["test"]["url"]
if __name__ == '__main__':
conf_read = ConfigYaml()
print(conf_read.get_conf_url())
#以上方法用来读取yaml配置文件的url,以下代码用来在测试用例中获取url
conf_y = ConfigYaml()
url_path = conf_y.get_conf_url()
url = url_path + "/tags/get"
三、日志文件
logging模块是python内置的标准模块,主要用于输出运行日志,可以设置输出日志的等级、日志保存路径等
#log_demo.py
#1、导入logging包
import logging
#2、设置配置信息
logging.basicConfig(level=logging.INFO,format = '%(asctime)s - %(name)s-%(levelname)s-%(message)s')
#3、定义日志名称getlogger
logger = logging.getLogger("log_demo")
#4、info、debug
logger.info("info")
logger.debug("debug")
logger.warning("warning")
日志输出控制台或文件:
1、设置logger名称
2、设置log级别
3、创建hander,用于输出控制台或写入日志文件
4、设置日志级别
5、定义handler的输出格式
6、添加handler
#输出控制台
import logging
# 1、设置logger名称
logger = logging.getLogger("log_file_demo")
# 2、设置log级别
logger.setLevel(logging.INFO)
# 3、创建handler
fh_stream = logging.StreamHandler()
# 4、设置日志级别
fh_stream.setLevel(logging.INFO)
#5、定义输出格式
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname) %(message)s')
fh_stream.setFormatter(formatter)
#6、添加handler
logger.addHandler(fh_stream)
#7、运行输出
logger.info("this is a info")
logger.debug(fh_stream)
#写入文件
import logging
# 1、设置logger名称
logger = logging.getLogger("log_file_demo")
# 2、设置log级别
logger.setLevel(logging.INFO)
# 3、创建handler
fh_stream = logging.StreamHandler()
#写入文件
fh_file = logging.FileHandler("./test.log")
# 4、设置日志级别
fh_stream.setLevel(logging.INFO)
fh_file.setLevel(logging.INFO)
#5、定义输出格式
formatter = logging.Formatter('%(asctime)s %(name)s %(levelname)s %(message)s')
fh_stream.setFormatter(formatter)
fh_file.setFormatter(formatter)
#6、添加handler
logger.addHandler(fh_stream)
logger.addHandler(fh_file)
#7、运行输出
logger.info("this is a info")
logger.debug("this is a debug")
四、pytest基本使用
pytest demo:步骤:1、创建普通方法 2、创建测试方法 3、pytest断言
import pytest
def func(x):
return x+1
@pytest.mark.flaky(reruns=3,reruns_delay=2) #部分失败重跑,--reruns n --reruns-delay(出错重试的时间)
def test_a():
print("---test a---")
assert func(3) == 5
def test_b():
print("---test b ---")
assert func(4) == 5
if __name__ == '__main__':
pytest.main(["-vs","pytest_demo.py"])
setup teardown:函数级别的方法,运行于测试方法的始末;
setup_class teardown_class:类级别方法,运行于测试类始末
import pytest
#1、定义类
class TestFunc:
# 3、创建setup teardown
def setup(self):
print("setup")
def teardown(self):
print("teardown")
#2、创建测试方法test开头
def test_a(self):
print("testaaa")
def test_b(self):
print("testbbb")
# 4、运行查看结果
if __name__ == '__main__':
pytest.main(["-vs","pytest_func.py"])
import pytest
class TestClass:
def setup_class(self):
print("setup")
def teardown_class(self):
print("teardown")
def test_a(self):
print("testaaa")
def test_b(self):
print("testbbb")
if __name__ == '__main__':
pytest.main(["-vs","pytest_class.py"])
parametrize参数化:1、单个参数 2、多个参数
import pytest
#传入单个参数:@pytest.mark.parametrize(argnames,argvalues)第一个参数为参数名 第二个参数为参数对应值,类型必须为可迭代类型,一般使用list
# 1、创建类和测试方法
class TestDemo:
# 2、创建测试数据
data_list = ["xiaoxiao","xixi"]
# 3、参数化
@pytest.mark.parametrize("name",data_list)
def test_a(self,name):
print("test_a")
print(name)
assert 1
if __name__ == '__main__':
pytest.main(["-vs","pytest_one.py"])
import pytest
#传入多个参数 list的每一个元素都是一个元祖,元祖里的每个元素和按参数顺序一一对应
# 1、创建类和测试方法
class TestDemo:
# 2、创建测试数据
data_list = [("xiaoxiao","123456"),("xixi","345678")]
# 3、参数化
@pytest.mark.parametrize(("name","password"),data_list)
def test_a(self,name,password):
print("test_a")
print(name,password)
assert 1
if __name__ == '__main__':
pytest.main(["-vs","pytest_two.py"])
断言:自动化最终目的,一个用例没有断言,就失去了自动化测试的意义了;assert;预期结果和实际结果做对比
常用断言
import pytest
def test_01():
a = True
assert a
def test_02():
a = True
assert not a
def test_03():
a = "hello"
b = "hello world"
assert a in b
def test_04():
a = b = "hello"
assert a==b
def test_05():
a="hello"
b = "hello world"
assert a != b
if __name__ == '__main__':
pytest.main(["pytest_assert.py"])
结果断言验证
1、断言应用接口用例:返回状态码、结果验证 返回的code、返回的body包含信息是否正确
#封装结果断言
import json
from utils.LogUtil import my_log
#1、定义封装类
class AssertUtil:
#2、初始化数据,日志
def __init__(self):
self.log = my_log("AssertUtil")
#3、code相等
def assert_code(self,code,expected_code):
try:
assert int(code) == int(expected_code)
return True
except:
self.log.error("code error,code is %s,excepted_code is %s"%(code,expected_code))
raise
#4、body相等
def assert_body(self,body,expected_body):
try:
assert body == expected_body
return True
except:
self.log.error("body error,body is %s,expected_body is %s"%(body,expected_body))
raise
#5、body包含
def assert_in_body(self,body,expected_body):
#验证返回的结果是否包含期望的结果
try:
body = json.dumps(body)
assert expected_body in body
return True
except:
self.log.error("不包含或者body是错误,body is %s,expected_body is %s"%(body,expected_body))
raise
#接口用例应用
from utils.AssertUtil import AssertUtil
code = r['code']
AssertUtil().assert_code(code,200)
# body = json.dump(r["body"])
# AssertUtil().assert_body(body,'"user_id":1,"username":"zzz"')
数据库结果断言
接口请求、返回结果验证
1、pymysql 2、工具类封装
#1、导包 2、连接database 3、获取执行sql的光标对象 4、执行sql 5、关闭对象
import pymysql
conn = pymysql.connect(
host = '',
user = '',
password='',
database='',
charset='',
port = 7090
)
cursor = conn.cursor()
sql = "select username,password from tb_users"
cursor.execute(sql)
res = cursor.fetchone()
print(res)
cursor.close()
conn.close()
1、创封装类 2、初始化数据、连接数据库、光标对象 3、创建查询、执行方法 4、关闭对象
import pymysql
from utils.LogUtil import my_log
class Mysql:
def __init__(self,host,user,password,database,charset = "utf8",port = 3306):
self.log = my_log()
self.conn = pymysql.connect(
host=host,
user=user,
password=password,
database=database,
charset=charset,
port=port
)
self.cursor = self.conn.cursor()
def fetchone(self,sql):
self.cursor.execute(sql)
return self.cursor.fetchone()
def fetchall(self,sql):
self.cursor.execute(sql)
return self.cursor.fetchall()
def exec(self):
try:
if self.conn and self.cursor:
self.cursor.execute(sql)
self.conn.commit()
except Exception as ex:
self.conn.rollback()
self.log.error("mysql执行失败")
self.log.error(ex)
return False
return True
def __del__(self):
if self.cursor is not None:
self.cursor.close()
if self.conn is not None:
self.cursor.close()
五、数据驱动
数据参数化:目的数据和代码进行分离
yaml数据驱动、excel数据驱动
1、编写yaml文件放置在data目录下 2、创建读取yaml文件的方法
列表方式:多个文档
---
"case_name": "success"
"url": "/cgi-bin/tags/create/"
"data":
tag:
name: "guangdong"
"except": '"code":200'
---
"case_name": "fail"
"url": "/cgi-bin/tags/create/"
"data":
tag:
name: ""
"except": '"code":200'
1、conf目录下conf.py添加data目录的路径 2、读取yaml文件路径、读取yaml文件内容
#获取路径
_data_path = BASE_DIR + os.sep + "data"
def get_data_path():
return _data_path
#1、获取测试用例内容:获取yaml文件路径、使用工具类读取多个文档内容
#2、参数化执行
import os
import pytest
from config import conf
from utils.YamlUtil import YamlReader
from config.conf import ConfigYaml
from utils.RequestsUtil import Request
#1、获取测试用例内容:获取testlogin.yml文件路径;使用工具类读取多个文档内容
test_file = os.path.join(conf.get_data_path(),'testlogin.yml')
# print(test_file)
data_list = YamlReader(test_file).data_all()
# print(data_list)
#2、参数化执行测试用例
@pytest.mark.parametrize("tagadd",data_list)
def test_yaml(tagadd):
url = ConfigYaml().get_conf_url()+tagadd["url"]
# print(url)
data = tagadd['data']
# print(data)
request = Request()
res = request.post(url,json = data)
print(res)
if __name__ == '__main__':
pytest.main(["-vs","test_tagadd.py"])
2、编写测试用例:读取excel数据(导入包:xlrd;创建wordbook对象;sheet对象;读取每行、读取每列、读取固定列的内容)
用例设计、excel读取、excel参数化
demo:把创建好的excel用例放在t_excel下,创建demo简单测试
#1、导包
import xlrd
#2、创建workbook对象
book = xlrd.open_workbook("testdata.xls")
#3、sheet对象
sheet = book.sheet_by_index(0)
#4、获取行数和列数
rows = sheet.nrows
cols = sheet.ncols
#5、读取每行、每列
for r in range(rows):
r_values = sheet.row_values(r)
# print(r_values)
for c in range(cols):
r_values = sheet.col_values(c)
# print(r_values)
#6、读取固定列的内容
print(sheet.cell(1,1))
excel工具类封装:
#目的:参数化,pytest, list
import os
import xlrd
class SheetTypeError:
pass
#1、验证文件是否存在,存在读取,不存在报错
class ExcelReader:
def __init__(self,excel_file,sheet_by):
if os.path.exists(excel_file):
self.excel_file = excel_file
self.sheet_by = sheet_by
self._data = list()
else:
raise FileNotFoundError("文件不存在")
#2、读取sheet名称 索引
def data(self):
#存在不读取,不存在读取
if not self._data:
wordbook = xlrd.open_workbook(self.excel_file)
if type(self.sheet_by) not in [str,int]:
raise SheetTypeError("请输入整数或字符串")
elif type(self.sheet_by) == int:
sheet = wordbook.sheet_by_index(self.sheet_by)
elif type(self.sheet_by) == str:
sheet = wordbook.sheet_by_name(self.sheet_by)
#3、读取sheet内容
#返回list 字典 [{"a":11,"b":"12"} {"a":22,"b":b2}]
#1、获取首行信息 2、遍历测试行 与首行组成dict 放在list
title = sheet.row_values(0)
for col in range(1,sheet.nrows):
col_value = sheet.row_values(col)
self._data.append(dict(zip(title,col_value)))
#4、结果返回
return self._data
if __name__ == '__main__':
reader = ExcelReader("../data/testdata.xls","Sheet1")
print(reader.data())
excel参数化运行:获取是否运行、参数化、结果断言
from utils.ExcelUtil import ExcelReader
#1、使用excel工具类,获取结果list
reader = ExcelReader("../data/testdata.xls","Sheet1")
# print(reader.data())
#2、列是否运行内容 y
run_list = list()
for line in reader.data():
if line["是否运行"] == "y":
# print(line)
#3、保存要执行结果,放到新的列表
run_list.append(line)
print(run_list)
将excel参数化 封装为方法:
#common-dataconfig:
#定义类 定义列属性
class DataConfig:
case_id = "用例ID"
case_model = "模块"
case_name = "接口名称"
url = "请求url"
pre_exec = "前置条件"
method = "请求类型"
params_type = "请求参数类型"
params = "请求参数"
expect_result = "预期结果"
actual_result = "实际结果"
is_run = "是否运行"
headers = "headers"
cookies = "cookies"
code = "status_code"
db_verify = "数据库验证"
#common-exceldata.py
from utils.ExcelUtil import ExcelReader
from common.ExcelConfig import DataConfig
class Data:
#1、使用excel工具类,获取结果list
def __init__(self,testcase_file,sheet_name):
# self.reader = ExcelReader("../data/testdata.xls", "Sheet1")
self.reader = ExcelReader(testcase_file,sheet_name)
#2、列是否运行内容 y
def get_run_data(self):
#根据是否运行列==y 获取执行测试用例
run_list = list()
for line in self.reader.data():
if str(line[DataConfig().is_run]).lower() == "y":
# print(line)
#3、保存要执行结果,放到新的列表
run_list.append(line)
print(run_list)
return run_list
#conf获取路径
def get_excel_file(self):
#获取测试用例名称
return self.config["BASE"]["test"]["testcase_file"]
def get_excel_sheet(self):
#获取测试用例名称
return self.config["BASE"]["test"]["case_sheet"]
初始化信息,执行测试用例
import json
import pytest
from config.conf import ConfigYaml
import os
from common.ExcelData import Data
from utils.LogUtil import my_log
from common import ExcelConfig
from utils.RequestsUtil import Request
#1、初始化信息
#1)初始化测试用例文件
case_file = os.path.join("../data",ConfigYaml().get_excel_file())
#2)测试用例sheet名称
sheet_name = ConfigYaml().get_excel_sheet()
#3)获取运行测试用例列表
run_list = Data(case_file,sheet_name).get_run_data()
#4)日志
log = my_log()
#2、测试用例方法,参数化运行
#一个用例的执行
#1、初始化信息 2、接口请求
class TestExcel:
#1、增加pytest 2、修改方法参数 3、重构函数内容 4、pytest.main
@pytest.mark.parametrize("case",run_list)
def test_run(self,case):
data_key = ExcelConfig.DataConfig
#run_list第一个用例 key获取values
url = ConfigYaml().get_conf_url()+case[data_key.url]
case_id = case[data_key.case_id]
case_model = case[data_key.case_model]
case_name = case[data_key.case_name]
pre_exec = case[data_key.pre_exec]
method = case[data_key.method]
params_type = case[data_key.params_type]
params = case[data_key.params]
expect_result = case[data_key.expect_result]
actual_result = case[data_key.actual_result]
is_run = case[data_key.is_run]
headers = case[data_key.headers]
cookies = case[data_key.cookies]
code = case[data_key.code]
#接口请求
request = Request()
#params转义json
if len(str(params).strip()) is not 0:
params = json.loads(params)
#method get post
if str(method).lower()=="get":
res = request.get(url,json = params)
elif str(method).lower()=="post":
res = request.post(url, json=params)
else:
log.error("错误的请求")
print(res)
if __name__ == '__main__':
pytest.main(['-s','test_excel_case.py'])
pytest.ini [pytest] addopts = -s --html=./report/report.html testpaths = testcase python_files = test_*.py python_classes = Test_* python_functions = test_*