接口自动化测试框架


接口自动化测试框架

一、市面行情

1.主流接口测试工具实现接口自动化 (适用于中小型的公司或项目) 50%

Postman+Newman+git/Svn+Jenkins (基于Javascript语言)接口自动化Jmeter+Ant+Git/Svn+Jenkins (基于Java和BeanShell语言)接口自动化
2.基于代码的接口自动化 (适用于大中型公司或一线互联网公司) 40%

Python+Requests+Yaml+Pytest+Allure+Logging+热加载+Jenkins持续集成接口自动化
3.基于平台的接口自动化 (适用于于特大型或外包型公司) 30K以上,10%测试开发

二、全面认识Requests

requests模块式用于发送Http请求以及接口http响应的python的第三方库。安装pip install requests
详解requests: Alt+Enter自动导包

def get(url, params=None, **kwargs):
def post(url, data=None, json=None,**kwargs):
def put(url, data=None, **kwargs):
def delete(url,**kwargs):
def request(method, url,**kwargs):#这个是前面四个方法统一调用的方法
def session():#回话,web项目中从登陆和退出就是一个回话.session对象的request方法
def request(self, method, url, params=None,data=None,headers=None,cookies=None,files=Noneauth=None,timeout=None,allow redirects=True,proxies=Nonehooks=None,stream=None,verify=None,cert=None,json=None):

解析requests底层原理:
method,请求方式
url, 请求路径
params=None,get请求传参

data=None,post或put请求传参

json=None post请求传参

headers=None,请求头

cookies=None,Cookie信息
files=None,文件上传
auth=None,鉴权
timeout=None,超时处理
allow_redirects=True,是否允许重定向
proxies=None,代理
hooks=None,钩子
stream=None,文件下载
verify=None,证书验证
cert=None,CA证书

Response对象:
res .text 返回文本格式

res .content 返回bytes类型数据

res.json() 返回json数据
res .status_code 返回状态码
res .reason 返回状态信息
res.cookies 返回cookie信息
res.encoding 返回编码格式
res .headers 返回响应头
res .request.??? 返回请求的信息和数据

post请求中data传参和json传参的本质

Postman中Post请求的四种不同的传参方式以及它们对应的请求头

文件上传: Content-Type:multipart/form-data (files)

表单: Content-Type:application/x-www-form-urlencoded (data)

文本raw:
Content-Type:application/json (json)
Content-Type:text/plain (data)
Content-Typeapplication/javascript (data)

Content-Type:text/html (data)
Content-Type:application/xml (data)
二进制:
Content-Type:application/octrent-stream (files)

三、Requests接口自动化测试实战

接口关联三个层次:
1.通过类变量保存中间变量实现接口关联。
2.通过单独的文件保存中间变量实现接口关联。
3.极限封装成零代码的方式实现接口关联。
接口关联两种方式:
(1)正则提取实现接口关联
re.seach(‘name=”csrf_ token” value= “ ( .*?)” ,res. text).group(1) 通过正则匹配一个值, 通过下标[1]取值,没有匹配到返回None.
re.findall()通过正则匹配多个值,返回List, 通过下标取值,没有匹配到返回None
(2)JsonPath提取实现接口关联
jsonpath.jsonpath(res.json(),”$.access_token”) 返回一 个列表, 通过下标取值,没有找到返回None.

res.json()[‘access_token’]

requests .request()和session.request)的区别:前者的每个请求都是独立的,后者会自动的关联,所有请求的cookie信息。

四、接口自动化框架封装 (统一请求封装)

import requests
class Requestutil:
    sess = requests.session()
#统一请求封装
    def send_ all_request(self,**kwargs ):
        res = RequestUtil.sess.request(**kwargs )
        print(res.text)
        return res

web项目的接口都会存在cookie关联。session对象能够自动的关联cookies

五、引入用例管理框架(单元测试框架)

python: unittest和pytest

java: junit和testing

unittest:ascii的大小来绝对的执行顺序,ord(‘a’):查看a的ascii码97;A 65
pytest默认从上到下的执行顺序

作用:
1.发现用例: 根据每个框架默认的规则去发现并且加载测试用例

2.执行用例: 将测试用例安装一定的顺序和条件执行,并生成结果

3.判断结果: 通过断言来判断实际结果和预期结果是否相同

4.生成报告:统计测试进度,耗时,通过率,生成报告

(2)优势
1.提高测试效率,降低维护成本
2.减少人工干预,提高测试的准确性,增加代码的重用性。
3.核心思想是让不懂代码的人也能够通过这个框架去实现自动化测试。

六、Pytest框架详解

pytest是一个非常成熟的python用例测试框架,它可以和很多的工具或框架,selenium,requests,appium实现多种自动化测试
它可以和allure结合生成美观的报告以及和jenkins实现持续集成最重要的是,它有很多的插件:

pytest-html  #生成html报告插件
pytest-xdist  #多线程
pytest-ordering  #标记测试用例的执行顺序@pytest.mark.run (order=1)
pytest-returnfailers  #失败用例重跑
pytest-base-url  #管理基础路径
allure-pytest  #生成allure报告
pytest 
requests
pyyaml

使用requirements.txt文件保存,并通过以下命令执行包装:

pip install -r requirements.txt

遇到:pytest-returnfailers安装失败,需要到https://pypi.org/中搜索对应的版本下载安装即可

基于pytest单元测试框架,默认测试用例的规则

1.模块名(py文件)必须以test_ 开头或 _test结尾
2.测试类名必须Test开头,且不能有init方法
3.用例名必须以test 开头

七、执行的方式有三种

1.通过main执行

import pytest
if __name__ =='__main__':#入口
    pytest.main(['-vs'])

(1)运行所有: pytest. main()
(2)指定模块: pytest. main([‘-vs’,’test_ login.py’])
(3)指定目录: pytest. main([‘-vs’,’./interface_ testcase’])

pytest. main([‘-vs’,’./interface_ testcase’,‘–html=report/report.html’,’–self-contained-html’])

(4)通过nodeid指定用例运行: nodeid由模块名,分隔符,类名,方法名,函数名组成。
pytest. main([‘-vs’,’./interface_ _testcase/test interface py::test 04 _func’])

pytest.main( [‘-vs‘,’/testcase’,’-n=2’])

2.通过命令行执行
pytest -vs

pytest -vs test_ login.py

pytest -vs ./interface_ _testcase/test interface.py::test 04 _func

执行的时候有一些参数:
-v:输出更详细的信息
-s:输出调试信息 (如打印)
-n:多线程运行测试用例

-q:保持输出简短

pytest -vs test_ login.py -n 2

-k:根据测试用例的部分字符串指定测试用例

pytest -vs test_ login.py -k “ao”

-x:表示只要有一个用例报错。那么测试停止

–maxfail=2出现两个用例失败就停止

–reruns 失败用例重跑
–html 生成HTML报告

从包运行测试

pytest --pyargs pkg.testing

读懂测试结果

可用字符的完整列表:

  • f -失败
  • E -误差
  • s 跳过
  • x -失败
  • X -XPASS
  • p 通过
  • P -通过输出

修改python回溯打印
修改回溯打印的示例:

pytest –showlocals # 在回溯中显示局部变量
pytest -l # show local variables (shortcut)

pytest –tb=auto # (default) ‘long’ tracebacks for the first and last

entry, but ‘short’ style for the other entries

pytest –tb=long # exhaustive, informative traceback formatting
pytest –tb=short # shorter traceback format
pytest –tb=line # only one line per failure
pytest –tb=native # Python standard library formatting
pytest –tb=no # no traceback at all

分析测试执行持续时间
在 6.0 版更改.

要获取长度超过1.0秒的最慢10个测试持续时间的列表:

pytest –durations=10 –durations-min=1.0

3.通过读取pytest.ini配置文件来执行测试用例不管使用main还是命令行都会自动的去读取pytest.ini的配置文件

[pytest]
#命令行参数
addopts = -Vs
#改变默认的测试用例的路径
testpaths = ./testcase
#改变测试用例的模块名规则
python_files = test_*.py
#改变测试类的默认规则
python_classes = Test* 
#改变测试用例名称规则
python_functions = test_* 
#设置基础路径
base url = http://www.baidu.com
#标记执行:
markers =
    smoke:冒烟测试
    login:登陆用例
    product_manage:商品管理
    user_manage:用户管理

分组执行标记的案例

在用例上加@pytest.mark.smoke和@pytest.mark.product_manage

pytest -m smoke 运行所有用 @pytest.mark.smoke 装饰符

pytest -vs -m “smoke or product_manage” 两种装饰器都执行

pytest跳过测试用例
(1)无条件跳过
@pytest . mark. skip (reason=”微微”)
(2)有条件跳过
@pytest . mark. skipif (age>=18, reason=’已成年’ )

@unittest.skipUnless(age<18,”如果知了成年,忽略”)

def test03 zhiliao(self):
print (“测试知了”)

八、执行测试用例时的前后置处理

第一种:和unittest相似

模块级别: setup_module、teardown_module
函数级别:setup_function、teardown_function,不在类中的方法
类级别: setup_class、teardown_class

方法级别:setup_method、teardown_method,在类中的方法
用例级别:setup、teardown

def setup_class(self):
    print("每个类之前执行")
def teardown_class(self):
    print("每个类之后执行")
def setup(self) :
    print("用例之前执行")
def teardown(self)
    print("用例之后执行")

第二种: fixture固件,作用就是可以更加随心所欲的设置前后置

使用装饰器
@pytest.fixture(scope=””,params=””autouse=,ids=””,name=””)

scope:作用域
function(函数,用例,默认)
class 类

​ module 模块
​ package

​ session 会话
params:数据驱驱动。参数化(支持,列表,元祖(),字典列表[{},{},{}] ,字典元祖({},{},{})
autouse:自动作用还是手动作用

True自动调用,Fasle手动调用(需要在参数里面传入固件名称)

ids:当数据驱动时更改参数名
name:fixture的别名
案例:

@pytest.fixture(scope="function",autouse=False,params=read_yaml(),ids=['bl', 'bf' , 'xy'], name="ea" )
def exe_assert(request) :
    print("在用例之前执行: 查询数据库用于断言")
    yield request.param #生成器
    print("在用例之后执行: 查询数据库”)
@pytest.fixture()
    def driver():
driver = get_webdriver(#启动浏览器yield driver # 这是关键
driver.quit(#关闭浏览器

return和yield都表示返回的意思, 但是return的后面不能有代码,yield返回后后面可以接代码。

params=[‘成龙’,’甄子丹’,’菜10’] 这里params是参数名,有s。 request.param这里是属性名,是没有s的。

第三种:通过conftest.py和@pytest.fixture()结合使用实现全局的前后置处理(比如:项目的全局登录.模块的全局处理)
1.conftest py文件是单独存放的一个夹具配置文件,名称是不能更改。
2.用处可以在不同的py文件中使用同一个fxture函数。

3.conftest py需要和运行的用例放到统一层。 并且不需要做任何的imprt导入的操作。

封装更加合理的fixture

摆脱用例和用例直接的依赖

pytest.fixture(scope="module")
def driver():
"""未登录的浏览器"""
    driver = get_webdriver()#启动浏览器
    yield driver
    driver.quit() #关闭浏览器
@pytest.fixture(scope="module")
def user_driver():     #己登录的浏览器
    driver = get_webdriver()
    driver.get(LoginPage.ur1)
    page = LoginPage(driver)
    page.1ogin("beifan_0609""123123")
    page.wait_url_chenge()    #等待登录完成,页面跳转
    yield driver
    driver.quit()
@pytest.fixture(scope="modu1e")
def c1ear_favor(user_driver):
#1.跳转到个人中心商品收藏页面
# 2.对页面进行操作,清除已有的收藏数据
user_driver.get(userGoodsFavorPage.ur1)
page = userGoodsFavorPage(user_driver)
page.delete_all()  

九、YAML详解

yaml简介:yaml是一种数据类型,它可以和json之间灵活的切换,支持:注释,换行,字符串,裸字符串等。

用途: 1.配置文件 2.编写测试用例

安装:pip3 install PyYaml

数据结构:

1.Map对象(dict)(键:(空格)值) 如:name: 百里

2.数组(list),用一组横线”-“开头来表示 如:

yaml读取单个文档:yaml.safe_load()

- username: "test1"
- password: "111"
  username1: "test2"

yaml读取多个文档:yaml.safe_load_all()

---
username: "test1"
password: "111"
---
username1: "test2"
password1: "123"
#用例
-
    name: 获得token鉴权码的接口
    request :
        url: https://api.weixin.qq.com/cgi-bin/token
        method: get
        headers:
            Content-Type: application/json 
        params: 
            grant_type: client_ credential
            appid: wx6b11b3efd1cdc290
            secret: 106a9c6157c4db5f 6029918738f9529d
    validate : 
        - eq: { expires_in: 7200 }
import yaml
class YamlUtil : 
    def __init__ (self, yaml_file) :
        """
        通过init方法把yam1文件传入到这个类
        :param yaml_file:
        """
        self.yaml_file = yaml_file
    #读取yam1文件
    def read yaml (self) :
        """
读取yam1,对yaml反序列化,就是把我们的yaml格式转换成dict格式。
        """
        with open (self.yaml_file,encoding='utf-8',) as f:
            value=yaml.load(f,Loader=yaml.FullLoader )
            print (value)
        return value 
   
    #写入:追加
    def write_yaml(self,data):
        with open(self.yaml_file,encoding="utf-8",mode="a+" ) as f:
        yaml.dump(data, stream=f, allow_unicode=True)
I

    #清空
    def clear_yaml():
        with open( self.yaml_file, encoding="utf-8" ,mode="w") as f:
        f.truncate()
import os
import yaml
#1、创建类
class YamlReader:
#2、初始化,文件是否存在
    def __init__(self,yamlf):
        if os.path.exists(yamlf):
            self.yamlf = yamlf
        else:
            raise FileNotFoundError("文件不存在")
        self._ data = None
        self._data_all=None
#3、yaml读取单个文档
    def data(self):
        #第一次调用data,读取yaml文档,如果不是,直接返回之前保存的数据
        if not self._data:
            with open(self.yamlf,"rb") as f:
                self.data =yaml.safe_load(f)
        return self._data
    def data_all(self): #yaml读取单个文档
        if not self._data_all:
            with open(self.yamlf,"rb") as f:
                self.data_all =yaml.safe_load_all(f)
        return self._data_all

yaml配置文件设置

BASE:
 test:
     url: "http://211.103.136.242:8064"
import os
#1、获取项目基本目录
#获取当前项目的绝对路径
current =os.path.abspath(__file__)#print(current)
BASE_DIR = os.path.dirname(os.path.dirname(current))#print(BASE DIR)
#定义config目录的路径
config_path=BASE_DIR + os.sep +"config'
#定义conf.yml文件的路径
config_file =_config_path + os.sep +"conf.yml"
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"]

十、Pytest用例管理框架数据驱动详解

python的反射
正常:先初始化对象,在调方法。
反射:通过对象得到类对象。然后通过类对象调用方法

数据驱动装饰器
@ pytest.mark.parametrize(ages1,args2)

args1:参数名: 用于传值。
vars2:参数值 (可以为列表或元祖,字典列表,字典元祖),数据中有多少个值那么用例就会执行多少次。

class TestLogin:
    @pytest.mark.parametrize("caseinfo",["敏敏","北北","星星"])
    def test_login(self,caseinfo):
        print("登陆接口:+caseinfo)
    @pytest.mark.parametrize("name,age"[["敏敏","13"],["北北","12"],["星星","11"]]) #第二种方式
    def test_register(self,name, age):
        print("注册接口: %s"% name,age)

第二种方式:跟unittest的ddt里面的@unpack解包的一样

excel工具类

import xlrd
import os
#目的:参数化,pytest list
#自定义异常
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:      
            workbook =xlrd.open_workbook(self.excel_file)
            if type(self.sheet_by) not in [str,int]:
                raise SheetTypeError("请输入Int or Str")
            elif type(self.sheet_by)== int:
                sheet = workbook.sheet_by_index(self.sheet by)
            elif type(self.sheet_by)==str:
              sheet=workbook.sheet_by_name(self.sheet_by)
    #3、读取sheet内容,list内嵌字典
        #1.获取首行的信息
            title =sheet.row_values(0)
        #1 循环,过滤首行,从1开始
            for col in range(1,sheet.nrows):
                col_value= sheet.row_values(col)
         #2 与首组成字典,放list
                self._data.append(dict(zip(title, col_value)))
    return self._data

十一、pytest结合allure生成美观的报告

1.下载allure,下载之后解压,解压之后配置path环境变量.需要配置jdk,jre

使用pycharm配置JDK 或者]RE
D: \Program Files \JetBrains Pycharm Community Edition 2019.2.2 \jbr
将目录添加到环境变量JAVA HOME

path路径的配置: D:\allure-2.13.7\bin (路径中不能有中文)

验证: allure –version (需要在dos和pycharm中都要验证)如果说Pycharm中验证失败,那么需要重启pycharm,让环境生效。

feature:一级标签,对应excel的sheet名称

story:二级标签,模块

title:用例ID+接口名称

description:请求URL 请求类型 期望结枼 实际结果

Severity:定义用例的级别,主要有BLOCKER,CRITICAL,MINOR,NORMAL,TRIVIAL等几种类型
,默认是NORMAL

@allure.title("测试用例标题2)
@allure.description("执行测试用例1的结果是test2")
@allure.story("这是一个二级标签:test1')
@allure.severity(allure.severity_level.CRITICAL)
    def test(self):
        print("test2")

Allure.dynamic:动态设置相关配置

#写在用例里面
#sheet名称 feature 一级标签
allure.dynamic.feature(sheet name)
#模块 story 二级标签
allure.dynamic.story(case model)
#用例ID+接口名称 title
allure.dynamic.title(case id+case name)
#请求URL 请求类型 期望结果 实际结果描述
desc ="<font color="red">请求URL:</font>{} <Br/> \
<font color="red">请求类型:</font>{} <Br/> \
<font color="red">期望结果</font>{} <Br/> \
<font color="red">实际结果</font>{}".format(url,method,expect_result,res)
allure.dynamic.description(desc)

2.在pytest.ini文件中加入命令

[pytest]
addopts = -vs --alluredir=./temps --clean-alluredir
#执行目录
testpaths = testcase
#执行目录下的文件
python_files = test_*.py
#执行测试类#
python_classes = Test_*
#执行测试方法
python_functions =test_*

3.生成Allure报告

os.system("allure serve .allure_results")

在cmd窗口中执行

allure generate .temps -o report --clean

allure generate 命令,固定的

./temp 临时的json格式报告的路径

-o 输出output

./report 生成的allure报告的路径

–clean 清空./report 路径原来的报告

查看报告
必须使用HTTP协议对报告[目录]进行访问
应该是url

日志

在pytest.ini文件中添加 log_file_level = info

在每个页面类中加入

loggers = logging.getLogger( __name__)

在测试用例的开始或结束加上logger.info(“打印信息”)

截图

在用户用例的执行前后截图加入到allure报告中

allure.attach(
    # 交互后进行截图,确认交互效果
    se1f._driver.get_screenshot_as_png(),
)

Subprocess介绍
允许你产生新的进程,并且可以把输入,输出,错误直接连接到管道,最后获取结果
官方网站:>https://docs.python.org/2/library/subprocess.html
方法:>subprocess.call()

父进程等待子进程完成,返回退出信息(returncode,相当于Linux exit code)

shell=True 的用法,支持命令以字符串的形式传入。

def allure_report(report_path,repart_html)
#执行命令 allure generate
    allure_cmd ="allure generate %s -o %s --clean"%(report_path,repart_html)
    log.info("报告地址”)
    try:
        subprocess.call(allure_cmd,shell=True)
    except:
        log.error("执行用例失败,请检查一下测试环境相关配置")
        raise

十二、Web端自动化测试:

1.极高的效率执行

2.完成复杂的业务场景

3.记录和调试网页的细节
目前主流的工具:
Cypress
Playwright
Selenium

1.Selenium自动化环境搭建

pip insta11 webdriver_helper-0.0.15-py3-none-any.whl
from webdriver_helper import get_webdriver
driver = get_webdriver("ie") # 支持chrome、firefox 、ie
driver.get("https://baidu.com")# 控制浏览器
driver.quit() # 退出浏览器

只支持Python3.9及以上版本

2.浏览器交互

selenium 以面向对象的思路,完成自动化
获取数据: 访问对象的属性
操作数据: 调用对象的方法
所以: 对浏览器的控制,对driver对象的方法进行调用

maximize_window() 最大化窗口

get(url) 跳转指定页面

refresh() 刷新

back() 后退

forward() 前进

get_screenshot_as_png() 截图的base64内容

get_screenshot_as_file(path) 截图保存到文件

current_url 网址

page_source 源码

current_window_handle 当前窗口

window_handles 所有窗口

switch_to.alert 处理弹窗

switch_to.frame(‘frame_name’) 切换框架

switch_to.window(‘window_name’) 切换窗口

from webdriver_he1per import get_webdriver
driver = get_webdriver()# 支持chrome、firefox . ie
driver.get_screenshot_as_file("1_启动.png")#文件名字必须png结尾
driver.maximize_window()
driver.get_screenshot_as_file("2.最大化.png")
driver. get("https: // baidu. com")#控制浏览器
driver.get_screenshot_as_file("3.访问baidu. png")
print("当前的网址: ",driver.current_ur1)
print("当前的标题:",driver.title)
print("当前的HTML: ",driver. page_source)
driver.quit()#退出浏览器

driver.switch_to.frame(id)
driver.switch_to.default_content() #

data=driver.window_handles #[s001,s002]

driver.switch_to.window(data[0])

ac = ActionChains(driver)

#手动获取滑块

ele = driver.find_element(By.XPATH,’//*[@id=”box”]/div[3]’)

ac.click_and_hold(ele).move_by_offset(300,0).perform()

ac.release()

3.元素定位

浏览器已经被webdriver-helper启动好了,可以直接进行交互元素需要被定位到,才能对其进行交互
Selenium提供8个定位策略

定位器 描述

id 定位id属性与搜索值匹配的元素

name 定位name属性与搜索值匹配的元素

tag_name 定位标签名称与搜索值匹配的元素

class_name 定位class属性与搜索值匹配的元素(不允许使用复合类名)

link_text 定位link_text可视文本与搜索值完全匹配的锚元素

partial_link_text 定位link_text可视文本部分与搜索值部分匹配的锚点元素

xpath 定位与XPath表达式匹配的元素

css_selector 定位CSS 选择器匹配的元素

1.根据属性进行定位

元素定位的时候:满足定位条件的元素,可能不只一个

<input
id="kw"    #只有ID属性,有唯一性的约束
name="wd"
class="s_ipt"
value=""
max1ength="255"
autocomplete="off"
>
driver = get_webdriver( #支持chrome、firefox . ie
driver.get("https://baidu.com")#控制浏览器
e1_1 = driver.find_element(By.TAG_NAME,"input")
e1_2 = driver.find_element(By.ID,"kw")
e1_3 = driver.find_element(By.NAME,"wd")
e1_4 = driver.find_element(By.CLASS_NAME,"s_ipt")
print(e1_1)
print(e1_2)
print(e1_3)
print(el_4)
e1_5 = driver.find_elements(By.TAG_NAME,"input")[7]
print(e1_5)
driver.quit()#退出浏览器

2.根据文本进行定位

只能定位a元素

<a
href="http: //news.baidu . com"target="_b1ank"
c1ass="mnav c-font-normal c-color-t">
新闻
</a>
driver.get("https://baidu.com")    #控制浏览器
el_1 = driver.find_element(By.LINK_TEXT,“新闻")#精确匹配
el_2 = driver.find_element(By.PARTIAL_LINK_TEXT,"新")#模糊匹配
print(e1_1)
print(e1_2)

3.根据表达式进行定位

driver = get_webdriver() #支持chrome、firefox . ie
driver.get("https://baidu.com")#控制浏览器
el_1 = driver.find_element(By.css_SELECTOR,"#kw")#精确匹配
el_2 = driver.find_element(By.XPATH,'//*[@id="kw"]')#模糊匹配
print(e1_1)
print(e1_2)
driver.quit() #退出浏览器

CSS和XPath 怎么选:
如果有前端开发配合,或自己就是前端开发:选择CSS

如果没有前端开发配合:选择XPath
支持层级跳转
支持布尔值,函数

5.XPath语法

//*[@id="kw" and @name="beifan"]

1.XPath基本语法

层级

属性

/ :(开头) 根路径 /html/body

//:(开头、中间) 任意层级 //*[@id=”kw”]

*:任意元素

@: 属性筛选

/: (中间)下一级 /html/body

.: 本级

.. :上一级/
(模糊匹配)
starts-with开头相同
(半模糊匹配)

3.XPath 调试
开发者工具—控制台—- $x(‘路径’)进行XPath调试
XPath 写出来之后,先在F12验证一下,然后放入测试代码

跳转指定网址:window.location.href=‘www.baidu.com’

模拟弹窗

开发者工具—控制台—-alert(“123”)

确认弹窗:confirm(“123”) 输入弹窗:prompt(“123”)

处理一闪而过的窗口

打开console,输入代码setTimeout(() => {debugger;}, 4000); 回车—->表示4秒后冻结窗口,除非手动结束,否则不会消失,这时候4秒内点击登录,弹出消息就被冻结了

开发者工具—控制台——–debugger暂定后选元素定位

解决id变化递增的问题
只能自己写xpath了

el = driver.find_element(By.XPATH, 
    '//*[starts-with(@id, "layui-layer")]/div[2]')
print(el.text)

4.元素交互

面向对象: 属性 方法

属性 内容 例子

id 唯一标记 36f-6a18

tag_name 标签名 input

location 元素坐标 {‘x’: 298, ‘y’: 188}

size 元素大小 {‘height’: 44, ‘width’: 548}

rect 元素范围 {‘height’: 44, ‘width’: 548, ‘x’: 298, ‘y’: 188}

parent WebDriver实例 略

screenshot_as_base64() 截图的base64内容 iVBORw0KGgoAAAAN….

screenshot_as_png() 截图的二进制内容 b’\x89PNG\r\n\x1a\n\x…

get_attribute(name) 获取元素的HTML属 性 value_of_css_property 获取CSS属性

click() 点击

clear() 清空内容

send_keys(content) 输入内容

三大等待

1.强制等待time.sleep(2)

2.隐式等待driver.implicitly_wait(10)# 大于0 的参数,表示启用隐式等待

3.显式等待自定义等待的时机 WebDriverWait(driver,5)

ele = webDriverWait(driver,10).until(
lambda _: driver.find_element(
By.XPATH,"/htm1/body/div[4]/div/u1/1i[3]/div/a/div/p")
)

5.POM

1.写出元素定位表达式 2. 写出合适等待方式 3. 维护各个元素的定位表达

Page Objects Model 页面对象模型

类 表示 页面

类属性 表示 页面中的元素

类的方法 表示 对页面的操作

封装po

定义过程:
1.定义 BasePage(抽象类)

2.定义Page(页面类)
3.定义元素的交互(对象方法)
使用过程:
1.打开页面,实例化Page

2.调用Page方法,完成交互

使用po

driver = get_webdriver(driver.get(LoginPage.ur1)
page = LoginPage(driver)#实例化PO
page.login("beifan_0609""123123")#登录msg = page.get_msg(#获取系统提示
assert msg =="登录成功"
driver.get(GoodsPage.ur1)
page = GoodsPage(driver)msg = page.add_to_cartO
assert msg =="加入成功"
driver.quit()

框架运行流程
1.在excel列出用户动作 (关键字)

⒉.框架读取、执行关键字
3.加载其他插件,实现并行测试、失败重试、HTML报告

Sanmu是一个纯用Excel描述测试用例的UT自动化测试框

默认执行run_pytest命令
options:
–help Show this message and exit.
Commands :
report 调用allure,生成HTML报告
run-pytest 启动Pytest框架执行用例,通过pytest-html生成报告

run-unittest 启动UnitTess框架执行用例,通HTMLTestRunner生成报告

show-keys 打印关键字文档
start 创建demo文件,并命名为test_{name}.xlsx

openpyxl + execl 数据驱动测试

新的案例实战:添加收获地址

1.编写测试用例

def test_user_new_address(user_driver):
    user_driver.get(UserAddressPage.url)
    page = UserAddressPage(user_driver)
    page = page.new_address() # 点击新增地址 按钮
    page_msg = page.submit(
        "北凡", "13191121211", "湖南省", "长沙市", "岳麓区", "桐梓坡路117号"
    ) # 输入数据 +提交表单
    assert page_msg == "操作成功"

2.加载execl测试数据

pip install openpyxl
def read_excel(filename):
    path = data_dir / filename
    wb = load_workbook(path)
    ws = wb.active
    yield from ws.iter_rows(min_row=2, values_only=True)

3.数据驱动测试

@pytest.mark.parametrize(
    "name,tel,province,city,county,address,alias,is_default,msg",
    read_excel("ddt_test_new_address.xlsx"),
)
def test_user_new_address(
    user_driver,
    name,
    tel,
    province,
    city,
    county,
    address,
    alias,
    is_default,
    msg,
):
    user_driver.get(UserAddressPage.url)
    page = UserAddressPage(user_driver)
    page = page.new_address() # 点击新增地址 按钮
    page_msg = page.submit(
        name,
        tel,
        province,
        city,
        county,
        address,
        alias,
    ) # 输入数据 +提交表单
    assert page_msg == msg

十三、Jenkins以及持续集成简介

jenkins是一个可以扩展的持续集成和持续部署的平台。它也只是一个平台,主要运行的都是插件

持续集成:把整个软件生命周期中的所有工作实现自动化,以流水线的方式去完成软件的研发过程
开发:编写代码并且进行源码管理,编译打包提供给测试人员测试
测试:部警测试环境进行功能测试,持续集成自动化测试

运维:部署线上环境

Jenkins的安装
1.安装JDK以及配置JDK的环境变量
2.插件的下载(1-2小时的下载 )

3访问:http//localhost:8080

Jenkins创建以及使用job(项目)应用
1新建job
2目定义工作空间:指定你的项目在哪里
3.输入执行命令:python all.py
4执行groovy脚本:System.setProperty(“hudson.model.DirectoryBrowserSupport.CSP”,””)

Jenkins集成Allure报告
1.安装allure插件: Allure Jenkins Plugin
2.在[全局工具配置Global Tool Configuration],配署Allure
3.在job配置allure报告

Jenkins持续集成企业微信(钉钉)实战
1创建企微群管理机器人,获得webhook
webhook
https//avapi.weixinggcom/cgi-bin/webhook/send?kev=bed4f5d11e1-4694-96a4-6f3be8a17197
2安装插件:Qv wechat…
3.在[系统配置]添加默认webhook地址

4.进入job,添加构建后操作。配置企微通知

Jenkins集成电子邮件通知实战

1.安装插件: Email…
2.新建一个邮箱。建议用163,企业邮箱肯定可以。
设置打开POP3(接受邮件的协议)和SMTP(发邮件的协议),获取的客户端的密码:bfwrrjqrzhxldgea

163 : BKFUMLHFHQBYGUPV
3.在[系统配置]配置邮件的发件人,SMTP服务器,收件人信息

4.进入job,添加构建后操作。配置邮件通知

系统管理员邮件地址必须和发件人保持一致

class EmaiManage :
    def send email(self,report_name):
        #定义SMTP服务器
        smtpserver =smtp.163.com
        #发送邮件的用户名和客户端密码
        username='mashang_XXX@163.com'
        #授权密码password =KNLOCNORUHWHIXJQ
        #接收邮件的邮箱
        receiver =XXX@gq.com
        #创建邮件对象
        message = MIMEMultipart('related')
        subject ='湖南一网通项目自动化测试报告' #邮件的主题
        fujian = MIMEText (open(report_name,'rb').read(),"html','utf-8')    #附件
        #把邮件的信息组装到邮件对象里面
        message['form'] = username
        message['to'] = receiver
        message['subject'] = subject
        message.attach(fujian)
        #登录smtp服务器并发送邮件
        smtp = smtplib.SMTP()
        smtp.connect(smtpserver)
        smtp.login(username,password)
        smtp.sendmail(username,receiver,message.as_string())
        smtp.quit()                

十四、Unittest框架

1.用例编写规则
unittest(python自带): 提供了testcases测试用例、testsuite测试套件、testfixtures测试件或夹具、testloader测试加载器,testrunner测试运行器。必须遵守以下规则:
(1)测试文件必须先导入import unittest
(2)测试类必须继承unittest.TestCase
(3)测试方法必须以test开头。
pytest:它是python的第三方测试框架。基于unittest的扩展框架,必须遵守以下规则:

(1)测试文件名必须以test 开头或者是 test结尾
(2)测试类命令必须以Test开头
(3)测试方法必须test开头

2.用例的前后置

setUp/tearDown 在每个用例之前或之后与运行一次
例如:打开浏览器,加载网页/关闭网页

setUpClass和tearDownClass 在每个类运行之前或之后运行一次

class Test(uittest.TestCase):
    @classmethod    #在那类中需要加标注
    def setUpClass(cls) -> None:
        print("setupclass:在每个类之前执行一次。如: 创建数据库,生成日志对象”)

例如:创建数据库连接,创建日志对象/关闭数据库连接,销毁日志对象

setUpModule和tearDownModule 在每个模块之前和之后执行一次

3.断言
unittest : assertTrue(a)/assertFalse(a) , assertEqual(a,b)/assertNotEqual(a,b) , assertln(a,b):断言a在b中

pytest : assert

4.报告
unittest : htmltestrunner

pytest : 插件 : pytest-HTML ,allure
5.失败重跑
unittest:没有

pytest: pytest-rerunfailures插件

6.数据驱动
unitest : ddt
pytest: @pytest.markparametrize装饰器
7.用例分类执行
unittest: 默认执行所有,也可以通过testsuite来执行部分用例,或者-k参数

pytest : @pytest.mark

TestCase用法

import unittest
class EcshopLogin(unittest.TestCase):
#测试用例
    def test01_baili(self):
        print(”测试用例")

1.使用unittest命令行的方式运行python -m unittest -v ecshop_login.EcshopLogin
python-m 以脚本的方式去运行一个模块

unittest-v–verbose 意思就是更详细的输出结果

ecshop_login.EcshopLogin 为模块名.类名.方法名

2.使用unittest.main(),以模块的方式运行

配置pycharm的运行环境或者在命令行使用:python 模块名.py

读懂测试结果
结果缩写符号
符号 含义

. 通过
F 失败
E 出错
X XPass意外通过
x xfailed 预期失败

S 跳过

x表示反义词
如果通过,什么都不显示
如果失败,则显示代码和报错信息
通过参数可以让结果更加的丰富

unittest.main()底层参数

module=’__main__’,测试用例用例所在的路径main 表示当前模块

defaultTest=None, 待测用例的名称,默认是所有
argv=None, 接收外部的参数
testRunner=None, 测试运行器,TextTestRunner

testLoader=loader.defaultTestLoader, 指定使用默认的测试用例加载器
exit=True 是否在测试完成之后结束python程序
verbosity=1 类似于命令行-V,0,1,2

只运行部分用例使用testCase

suite = unittest.Testsuite()
suite.addTest(EcshopLogin("test01_baili"))
suite.addTest(EcshopLogin("test02_weiwei"))
unittest.main(defaultTest=' suite') #本质上是和下面的命令一样效果的
#unittest.TextTestRunner().run(suite)
------------------------------
suite = unittest.TestSuite()
testcases = unittest.defaultTestLoader.discover(start_dir=osgetcwd(),pattern="*.py")
suite.addTests(testcases)
unittest.main(defaultTest='suite')

批量执行用例生成HTML格式的报告
在网上下载HTMLTestRunner.py文件放到python安装路径下:C:\python373\Lib

数据驱动ddt:DDT:Data Driver Tests

ddt 是第三方模块,需安装:

pip install ddt

DDT包含类的装饰器ddt和方法装饰器data
通常情况下,data中的数据按照一个参数传递给测试用例,如果data中含有多个数据,如元组,列表,字典等数据,需要自行在脚本中对数据进行分解或者使用unpack分解数据。

@ddt 类装饰器,用来标记当前类使用ddt数据驱动

@data 函数装饰器,用来给函数传递数据

@unpack 函数装饰器,用来对传递的数据进行解包,解列表、元组、字典

@file_data 函数装饰器,用来直接读取yaml,json格式的文件数据 读取获取的数据是字典列表

案例:直接在脚本文件中获取测试数据
@data(a,b)

那么a和b各运行一次用例

@data([a,b],[c,d])

如果没有@unpack,那么[a,b]当成一个参数传入用例运行

如果有@unpack,那么[a,b]被分解开,按照用例中的两个参数传递

import unittest
from ddt import ddt, data, unpack

case_data = [{"url": "www.baidu.com", "data": "test1"},
             {"url": "www.baidu.com", "data": "test2"},
             {"url": "www.baidu.com", "data": "test3"}]


@ddt
class TestDemo(unittest.TestCase):
    @data(*case_data) #在测试函数上使用@data(*case_data)进行数据解压,然后每一个元素传入测试函数
    def test_case(self, case):
        print(case)

运行结果:
Ran 3 tests in 0.005s

OK
{‘url’: ‘www.baidu.com’, ‘data’: ‘test1’}
{‘url’: ‘www.baidu.com’, ‘data’: ‘test2’}
{‘url’: ‘www.baidu.com’, ‘data’: ‘test3’}

import unittest
from ddt import ddt, data, unpack
@ddt
class MyTesting(unittest.TestCase):
    @data([1, 2, 3])  #没有@unpack,那么当成一个参数[1, 2, 3]
    def test_1(self, value):
        print(f'test_1:value={value}')

    @data(4,5,6)  #三个元素4,5,6
    def test_2(self, a):
        print(f'test_2:a={a}')

     @data([3, 2, 1], [5, 3, 2], [10, 4, 6])
     @unpack  #有@unpack,那么分解成3个参数传入用例运行:3, 2, 1是3个参数
     def test_minus(self, a, b, expected):
          actual = int(a) - int(b)
          expected = int(expected)
          self.assertEqual(actual, expected)

      @data([2, 3], [4, 5])  #没有@unpack,那么当成一个参数:[2, 3]是一个参数,[4, 5]是一个参数
      def test_compare(self, a):
          print(f'test_compare:a={a}')
if __name__ == '__main__':
    unittest.main(verbosity=2)

结果如下:
OK
test_1:value=[1,2,3]

test_2:a=4

test_2:a=5

test_2:a=6

test_compare:a=[2,3]

test_compare:a=[4,5]

yml文件中获取测试数据
在testdata package下新建testdata1.yml文件,文件中写入测试数据如下:

#格式要求:
#"-"表示一个列表
#用tab键缩进

-
  name: 'testyy'
  age:  18

-
  name: 'testzz'
  age:  20

test_ddt.py文件中修改测试代码如下:

import unittest
import ddt  # 导入ddt
import yaml
 
@ddt.ddt  # 声明我们要用它
class Test_ddt(unittest.TestCase):
 
    def setUp(self):
        print("Before every test case!")
 
    @ddt.file_data("..\\testdata\\testdata1.yml") #括号里写yml文件的相对路径
    def test_002(self,name,age):
        print('name is:' +str(name))
        print('age is:' + str(age))
 
if __name__ == '__main__':
    unittest.main()

yml文件中我们定义了2组数据,用例总共执行了2次,按顺序每次取一组数据作为入参。

从json文件中获取测试数据
在testdata package下新建testdata2.json文件,文件中写入测试数据如下:

{
   "student1": {
       "name": "小明",
       "age": 12
   },
   "student2": {
       "name": "小张",
       "age": 20
   }
}

test_ddt.py文件中修改测试代码如下:

import unittest
import ddt  # 导入ddt
import yaml
@ddt.ddt  # 声明我们要用它
class Test_ddt(unittest.TestCase):
    def setUp(self):
        print("Before every test case!")
                            @ddt.file_data("..\\testdata\\testdata2.json")#括号里写json文件的相对路径
    def test_003(self,name,age):
        print("name is:"+str(name))
        print("age is:" +str(age))
if __name__ == '__main__':
    unittest.main()

参考网址

十五、RobotFramework的简介和特点

简介:RF是一个基于Python语言开放的,可扩展的,以关键字驱动模式的自动化测试框架。
关键字驱动:关键字驱动表示把项目中的业务逻辑封装成一个一个的关键字,然后调用不同的关键字组成不同的业务。
数据驱动:数据驱动是把测试数据放到excel,jaml文件里面,然后通过改变文件中的数据去驱动测试用例执行。
特点:
1.编写用例非常方便,常见的有txt,robot
2.自动化生成html报告
3.非常强大的扩展库。
4.根据项目的需求自定义关键字
5.支持非GUI的方式运行,以及和jenkins实现持续集成

安装注意事项
1.安装python3.7以及环境变量
2安装robotframework
注意:最新的版本是4.1.2
安装的是3.1的版本pip install robotframework==3.1

3.安装RIDE开发工具:
命令 : pip install robotframework-ride
会弹出一个窗口,点确定,在卓面生成一个RIDE快捷方式。
豆辨下载源:

pip install i https://pypi.douban.com/simple robotframework==3.12 

pip install i https://pypi.douban.com/simple robotframework-ride

查看:pip list

使用RIDE开发工具
1.new project 注意: 选择Directory
2创建文件夫
3.创建测试套件
4.创建测试用例
5.创建资源文件(在文件夹下面创建资源文件,格式必须选择txt格式]。一个资源文件下面可以创建很多个用户目定义的关键字,资源文件可以在测试套件中导入并调用它下面的自定义关键字.

Robotframework常用的库
1.标准库(rf自带的库)
Builtin(测试库)
Collections(集合库)
DateTime(时间库)
Screenshot( 截屏库)
标准库保存的位置:C:\python373\Liblsite-packages\robotlibraries
2.扩展库(通过pip命令安装的库)

Web自动化测试:SeleniumLibrary Selenium2Library

​ 安装: pip install roboframework-seleniumlibrary

接口自动化测试: RequestsLibrary
安装: pip install roboframework-requests

APP自动化测试:AppiumLibrary
安装: pip install roboframework-appiumlibrary

扩展库保存的位置:C:\python373\Libisite-packages

常用关键字

快捷键:

F5 搜索关键字

F8 运行测试用例

Ctrl+Shfit+空格 自动补全关键字

Log 呵呵
Comment log 老师
${a} Set Variable 100
log ${a}
Sleep 3
${times} Get Time
Log ${times}
${content_str} Catenate SEPARATOR=# 大道 实施
log ${content_str}

结果:

呵呵

s(a)= 100

100

s{times} = 2023-10-14 20:59:30

2023-10-14 20:59:30

${content_str)=大道#实施

大道#实施

复杂关键字

${list1} Create List 百里 北凡 星瑶
log ${list1}
@{list1} Create List 百里 北凡 星瑶
Log Many @{list1}
${dic} Create Dictionary name=北里 age=18
log ${dic}
${keys} Get Dictionary Keys ${dic}
log ${keys}
${values} Get Dictionary ${dic}
log ${values}
${value_to_name} Get From Dictionary ${dic} age
Comment 直接执行python函数
${random_number} Evaluate random.randint(0,100) modules=random
log ${random_number}
${times} Evaluate time.time() modules=time
log ${times}
Comment 直接调用python自定义方法
Import Library E:/test.py
${a} Evaluate int(10)
${b} Evaluate int(20)
${he} sum ${a} ${b}
log ${he}

注意:对于没有 get dictionary keys方法,需要在Edit—>Library—>导入Collections

结果:

${list1} =[‘百里‘,‘北凡‘,‘星瑶]

[‘百里‘,‘北凡‘,‘星瑶]

@{list2}=[百里 | 北凡 | 星瑶 ]

百里

北凡

星瑶

${dic}={‘name’:’百里‘,’age’:18}

{‘name’:’百里‘,’age’:18}

${keys}=[‘age’,’name ‘]

[‘age’,’name ‘]

${values}=[‘18’,’百里’]

[‘18’,’百里’]

${value_to_name}=18

18

${random_number} = 40
40
${times} =1637759780.143498

1637759780.143498

${a}=10

${b}=20

${he}=30

30

${a} Set Variable 20
Run Keyword If ${a}>=80 log 成绩优秀
ElSE IF ${a}>=60 log 成绩及格
ELSE ifs ${a}
FOR ${a} IN 等等 查查 信息
log ${a}
END
FOR ${a} IN RANGE 1 11 2
RUN Keyword If ${a}==8 Exit For Loop
log ${a}
@{list1} Create List 百里 北凡 星瑶
FOR ${a} IN @{list1}
log ${a}
END

ifs为业务关键字文件中的if-else结构

断言AssertUtil

#1、定义封装类
class AssertUtil:
#2、初始化数据,日志
def __init__(self):
    #self.log = my_log("AssertUtil")
    pass
#3、code相等
def assert_code(self,code,expected_code):
    try:
        assert int(code)== int(expected_code)
        return True
    except:
        print("code error,code is %s,expected code is %s"%(code,expected_code))
#4、body相等
def assert_body(self,body,expected_body):
#验证返回结果内容相等
    try:assert body == expected_body
        return True
    except:
        print("body error,body is &s,expected body is %s"%(body,expected_body))
#5、body包含
def assert_in_body(self,body,expected_body):
#验证返回结果是否包含期望的结果
    try:
        body = json.dumps(body)
        assert expected_body in body
        return True
    except:
        print("不包含或者body是错误,body is %s,expected body is %s"%(body,expected_body))
        
        
        

数据库mysqlutil

import pymysql
class Mysql:
    #2、初始化数据,
    def __init__(self,host,user,password,database,charset="utf8",port=3306):
        self.conn = pymysql.connect(host=host,user=user,password=password,
                                    database=database,charset=charset,port=port)
        self.cursor =self.conn.cursor()
    #创建查询、执行方法
    def fetchone(self,sgl):
        self.cursor.execute(sql)
        return self.cursor.fetchone()
    def fetchall(self,sql):
        self.cursor.execute(sql)
        return self.cursor.fetchall()
    def exec(self): #update,delete,insert
        try:
            if self.conn and self.cursor:
                self.cursor.execute(sql)
                self.conn.commit()
        except Exception as ex:
            self.conn.rollback()
            print("执行异常")
            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()

python接口数据关联

class Context:
    #这是一个承接上下文的数据

前一个接口

使用setattr对其它接口需要的数据进行传递,加载到内存中去,比如说后一个接口需要 token

setattr(Context,"token",req.json()["data"]["token"])

后一个接口,使用getattr

getattr(Context,"tocken")

jemeter跨线程组实现接口关联

前提:第一个接口和第二个接口存在接口关联(通过token鉴权),同时还存在cookie鉴权。

接口压测:接口压力测试的任务,要求对其中一个接口进行500虚拟用户并发。但是这个接口必须要先 登陆之后获取到token才可以请求成功!

1.首先设置jmeter.propties里面的参数

CookieManager.save.cookies=true

才能够看到cookie

COOKIE_W2S_lastvisit=0%091652876410%09%2Fphpwind%2F
COOKIE_W2S_visitor=oF9u%2F8%2BTDp8OtNSRBld43kbit5MjxSfalRd7bm9lHA7Fy173WZoO1g%3D%3D
COOKIE_csrf_token=23f8b0cf8d839d02

2.跨线程组cookie鉴权: 只能在一个线程组里面传值的叫局部变量(正则,JSON,用户自定义变量) 能够在多个线程组之间传值的叫全局变量

需要通过Beanshell组件(既能使用java语言,也有自己的语法的组件)来设置:

__setPropty() 设置Jmeter的全局属性。能够在多个线程组之间传值

__P() 获取全局属性

埋点:

在测试报告的生成过程中埋点,拿到数据通过数据库操作存储到数据库

在conftest.py中,加入团队和负责人数据

impot pytest
@pytest.hookimpl(hookwrapper=True,tryfirst=True)
def pytest_runtest_makereport(item, call):
    #是通过yeild做轮询
    out = yield
    report = out.get_result()
    if report.when == "call": #固定过滤语句
        print(f"测试报告:{report}")
        print(f"步骤:{report.when}")
        print(f"node id:{report.nodeid}")
        print(f"执行结果:{report.outcome}")
        #下面的if 是获取我们 info 标签中注册的内容:比如我在用例中注册的是 team principal
        if "pytestmark" in item.function.__dict__:
            for i in item.function.pytestmark:
                if "info" in i.__dict__.values():
                    values =(report.location[0],report.location[-1],item.originalname,report.outcome,i.__dict__["kwargs"]["team"], i.__dict__["kwargs"]["principal"])
    #存储到数据库
                    mysql.change(f"""insert into test_cases(caseId,
,testId, test_name, result,team,principal) values {values};""")
                else:
                    values =(report.location[0],report.location[-1],item.originalname,report.outcome)
                    mysql.change(f"""insert into test_cases(caseId,
,testId, test_name, result) values {values};""")
      else:  
        values =(report.location[0],report.location[-1],item.originalname,report.outcome)
        mysql.change(f"""insert into test_cases(caseId,
,testId, test_name, result) values {values};""") 

使我们的测试用例携带其他维度的数据

在多个测试用例方法上加入

@pytest.mark.info(team=”第一团队”,principal=”长风”)

附录:接口自动化测试框架

文件夹加说明

commons: 公共封装文件夹

datas: YAML数据驱动文件夹

hotloads:热加载文件夹

logs:日志文件夹
reports:定制的allure报告文件夹

temps:临时报告文件夹
testcases:YAML测试用例文件夹

config.yaml:全局配置文件
conftest.py:全局fixture固件

extract.yaml:全局接口关联中间变量提取文件

pytest.ini:全局pytest配置文件

run.py:全局运行文件

readme:接口自动化测试框架说明文件

接口自动化测试框架YAML测试用例规范:

-
    feature: 用户相关
    story: 登陆b2c商城接口
    title: $ddt{title}
    request:
        method: post
        url: $(read_config(base_ur1)}?s=user/login
        data:
            accounts: $ddt{accounts]
            pwd: $ddt(pwd}
            type: $ddt{type}
    extract:
        token: $.data.token
    parametrize:
        -["title","accounts","pwd","type","assert_code","assert_msg"]
        - ["验证登陆成功测试用例","baili","baili123","username",0,"登录成功"]
        - ["验证密码必须是6-18位","baili","123","username",-1,"密码格式 6~18 个字符"]
        - ["验证错误的用户名",["baili"."北凡"],123,"username",-3,"登录帐号不存在"]
    validate:
        codes: 200
        equals:
            code: $ddt{assert_code}
        contains: $ddt{assert_msg}

一、一级关键字必须要包含: name,base url,request,validate,在一级关键字request下必须包含:method ,url

二、传参方式:
1.get请求,那么必须通过params传参
2.post请求
传json格式,需要使用json传参
传表单格式,需要使用data传参
3.文件上传:使用files传参
三、接口关联
支持多种提取方式: 正则表达式和jsonpath表达式提取

extract:

    access token:"access token":"(.*?)"         #正则
    expires in: $.expires in            #jsonpath

取值方式: 其中read_extract_data是热加载的方法名,access_token是提取的变量名

${read_extract _data(access token)}

四、热加载
1.在hotloads目录下创建一个py文件并新建一个类(可以自定义),然后写方法,注意返回值需要字符串就返回字符串,需要整形就返回征整形,如下:

#获得随机数,需要字符串返回的字符串。
def get_random_number(self,min,max):
    rm = random.randint(int(min), int(max))
    return str(rm)

2.在测试用例当中传入类的对象,如DebugTalk()

@pytest.mark.parametrize("caseinfo".read_testcase_yaml('/testcases/product_manage/pm_create flag.yaml'))
def test_create_flag(self, caseinfo):
    RequestsUtil(DebugTalk()).standard_yaml(caseinfo)

3.在YAML测试用例中调用函数: 如:
json:

{“tag”:[“name”:”码尚教育${get_random_number(10000,99999)}”}}

五、此框架支持两种断言方式: 分别为equals和contains断言
如:

validate:
- equals: {status code: 205}
- equals: {expires in: 7200}
- contains: access token

六、数据驱动使用yaml和一级关键字parameterize实现: 如:

测试用例yam1写法:
引入数据驱动文件
parameterize:
name-appid-secret-grant_type-assert_str:/datas/product manage/get_token_data.yaml
引入变量
$ddt{name}

数据驱动yam1写法:
#DDT(data driver test)数据驱动YAML文件,对应的YAML测试用例文件为:pm_get_token.yaml

prametrize:
    - ['name', 'appid','secret','grant_type','assert_str']
    - [成功获取token鉴权码','wx8a9de038e93f77ab','8326fc915928dee3165720c910effb86','client credential','accesstoken']
    - ['检查appid为空','8326fc915928dee3165720c910effb86','client_credential',41002]
    - ['检查secret为空','wx8a9de038e93f77ab','client_credential',41004]

七、基础路径的设置
在测试用例中使用一级关键字和热加载,如:

base_url: ${read_config(base,base_spgl_ur1)}


文章作者: 读序
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 读序 !
  目录