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

全部评论
写的太好了
点赞 回复
分享
发布于 02-22 21:25 上海
多谢佬
点赞 回复
分享
发布于 03-15 15:09 上海
滴滴
校招火热招聘中
官网直投

相关推荐

2 36 评论
分享
牛客网
牛客企业服务