测试工程师社招-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_*