Pytest单元测试框架

目录

单元测试框架

一、安装插件requirements.txt

二、配置文件pytest.ini

三、前后置

1、使用setup/teardown/setup_class/teardown_class

2、conftest.py中使用fixture实现部分前后置

四、测试用例

五、数据驱动封装

六、YAML详解

七、allure报告

1、生成原生allure报告

2、定制企业级allure报告

八、requests库详解

九、提取响应值


单元测试框架

单元测试框架是在自动化测试或者白盒测试中对软件的最小单元(函数,方法)进行测试的框架。

分为Python(unittest、pytest)和Java(Junit、Testing)

主要用来发现测试用例、执行测试用例、判断测试结果、生成测试报告

pytest是一个成熟、灵活且简单的单元测试框架。

可以结合selenium,requests,appium完成各种不同的自动化

一、安装插件requirements.txt

"""
在项目的根目录创建requirements.txt保存插件
再通过命令安装pip install --target=安装文件路径 -r requirements.txt
"""
# 生成html报告
pytest-html
# 改变用例的执行顺序
pytest-ordering
# 失败用例重新执行
pytest-rerunfailures                        
# 多线程运行
pytest-xdist
# 环境变量
pytest-base-url
# 生成allure报告
allure-pytest

pytest
requests
pyyaml

二、配置文件pytest.ini

[pytest]
# -v(输出更加详细的运行信息)
# -s(输出调试信息)
# -n=num(多线程运行)
# --reruns=num(失败用例重跑)
# -x(只要有一个失败用例就停止运行)
# --maxfail=num(只要有num个用例失败就停止运行)
# --html=报告的路径(生成html报告)
# -m "smoke"(只执行标记用例@pytest.mark.smoke)
addopts = -vs
# 测试用例所在目录
testpaths = ./testcases
# 模块名必须以test_开头
python_files = test_*.py
# 测试类名必须以Test开头
python_classes = Test*
# 测试方法必须以test_开头
python_functions = test_*
# base_url就是一个function级别的手动调用的fixture
base_url = http://101.34.221.219:8010/api.php
# 用例分组
markers =
    smoke: 冒烟测试
    product_manage: 商品管理

三、前后置

1、使用setup/teardown/setup_class/teardown_class

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

    def teardown_class(self):
        print("每个类之后执行一次")

2、conftest.py中使用fixture实现部分前后置

"""
@pytest.fixture(scope="function",autouse=False,params=[1,2,3],ids=None,name=None)
scope:作用域
    session
    package
    module
    class
    funton
autouse:是否自动执行
    True
        如果测试用例想要调用固件返回值,只需在测试用例参数中添加固件名称
    False
        针对class,在类头部加上装饰器@pytest.mark.usefixtures('固件名称')
        针对function,在测试用例中加入固件名称
params:实现参数化
    固件参数里request指向params
    固件体里request.param引用
ids:不能单独使用,必须和params一起使用,作用是对参数起别名
name:作用是给固件起别名,原固件名失效
"""
import pytest

@pytest.fixture(scope="session",autouse=True)
def all():
    print("在整个项目会话之前")
    yield
    print("在整个项目会话之后")

四、测试用例

跳过测试用例

@pytest.mark.skip(reason="无理由跳过")

@pytest.mark.skipif(条件,reason="条件成立时跳过")

改变测试用例执行顺序

@pytest.mark.run(order=XX)

断言

assert 1==1

五、数据驱动封装

import allure
import pytest
from common import yaml_util


class TestLogin:
    @pytest.mark.parametrize("caseinfo", yaml_util.YamlUtil().read_yaml("./testcases/user_manage/test.yaml"))
    def test_login(self, caseinfo, base_url):
        print(caseinfo)
import yaml


class YamlUtil:
    def read_yaml(self, path):
        with open(path, mode="rt", encoding="utf-8") as f:
            value = yaml.load(f, yaml.FullLoader)
            return value

六、YAML详解

-
  story: 登录接口
  title: 正例:验证登录成功测试用例
  request:
    method: post
    url: ?s=user/login
    headers:
      Content-Type: application/json
    params:
      application: app
      application_client_type: h5
    data:
      accounts: "cang"
      pwd: "123456"
      type: "username"
  validate: null
-
  story: 登录接口
  title: 反例:验证用户名错误
  request:
    method: post
    url: ?s=user/login
    headers:
      Content-Type: application/json
    params:
      application: app
      application_client_type: h5
    data:
      accounts: "c"
      pwd: "123456"
      type: "username"
  validate: null

1、定义

yaml是一种数据类型,扩展名为.yaml或.yml

2、作用

配置一些信息,例如环境变量、数据库信息、用户名密码、日志格式等

3、语法规则

        区分大小写

        通过缩进表示层级关系,一般用空格,不要使用tab键

        通过#注释

        字符串可以不用写引号,也可以写单引号或双引号。单引号和双引号的区别在于:单引号会对特殊字符进行转义

4、常用数据类型

        标量:整数、浮点数、字符串、布尔、Null、日期

        对象:键:(空格)值

        数组:通过在一组数据之前加"-"

5、数据类型强转(强转并不是在任何时候都成立)

!!str !!int !!float !!bool !!null !!timestamp !!set !!map !!binary

6、变量的引用

        &表示建立一个锚点

        *用来引用锚点

        <<表示合并

date1: &dates
  name: "Cang"
date2:
  name1: "百里"
  name2: "北凡"
  <<: *dates

七、allure报告

1、生成原生allure报告

(1)安装allure-pytest插件

(2)Releases · allure-framework/allure2 · GitHub下载allure,配置环境变量(把allure的bin目录配置到path中)

        注意版本兼容:python版本3.10、allure版本2.13、allure-pytest版本2.10、pytest版本7.1

(3)验证是否安装成功

        在doc窗口写入:allure --version

(4)生成临时的json报告

        在pytest.ini中写入:addopts = -vs --alluredir=./temps --clean-alluredir

(5)生成allure报告

        在run.py中写入:os.system("allure generate ./temps -o ./reports --clean")

2、定制企业级allure报告

(1)定制logo

修改D:\allure-2.18.1\config\allure.yaml,增加自定义logo插件:

plugins:
  - junit-xml-plugin
  - xunit-xml-plugin
  - trx-plugin
  - behaviors-plugin
  - packages-plugin
  - screen-diff-plugin
  - xctest-plugin
  - jira-plugin
  - xray-plugin
  - custom-logo-plugin

更改D:\allure-2.18.1\plugins\custom-logo-plugin\static\styles.css

.side-nav__brand{
    background: url('logo.png') no-repeat left center !important;
    margin-left: 10px;
    height: 100px;
    background-size: contain !important;
}

.side-nav__brand-text{
    display: none;
}

(2)定制allure报告功能页面

import allure
import pytest


@allure.epic("项目名称")
@allure.feature("模块名称")
class TestLogin:
    @allure.story("接口名称")
    @allure.title("用例名称")
    @allure.description("用例描述")
    @allure.severity(allure.severity_level.TRIVIAL)    # 致命BLOCKER,严重CRITICAL,一般NORMAL,提示MINOR,轻微TRIVIAL
    # @allure.link("访问接口的链接")
    # @allure.issue("bug链接")
    # @allure.testcase("测试用例的链接")
    def test_login(self):
        # 增加用例步骤
        with allure.step("用例步骤第一步:输入用户名"):
            pass
        # 增加用例附件
        with open(r"D:\shu.jpg", mode="rb") as f:
            content = f.read()
            allure.attach(body=content, name="错误截图", attachment_type=allure.attachment_type.JPG)
        allure.attach(body="http://www.baidu.com", name="接口请求地址", attachment_type=allure.attachment_type.TEXT)

(3)局域网里访问

在终端中输入:allure open D:\PycharmProject\api_frame\reports

八、requests库详解

1、发送http请求,接收http响应的库

pip install requests

2、常用方法

def get(url, params=None, **kwargs):
    url: 接口请求地址
    params: 自动以?的方式加到url之后,多个参数用&分割

def post(url, data=None, json=None, **kwargs):
    data: 用于传简单的键值对,或者字符串
    json: 用于传简单和复杂的键值对
    files: 用于传文件

    application/x-www-form-urlencoded: 表单            data
    multipart/form-data: 带有文件上传的表单             files
    application/json: JSON数据格式                     json
    text/plain、text/xml: 纯文本格式或者xml格式         data

def put(url, data=None, **kwargs):
def delete(url, **kwargs):
-----------------------------------------------------------------------------------
def request(method, url, **kwargs): 是get,post,put,delete的底层方法
-----------------------------------------------------------------------------------
requests.session().request: 是上面request的底层方法
def request(                
    self,
    method, 请求方式
    url, 请求路径
    params=None, params参数
    data=None, data参数
    json=None, json参数
    files=None, 文件上传
    headers=None, 请求头
    cookies=None, cookie信息
    --------------------------
    auth=None, 鉴权
    timeout=None, 超时处理
    allow_redirects=True, 重定向
    proxies=None, 代理
    hooks=None, 钩子
    stream=None, 文件下载
    verify=None, 证书验证
    ):

3、request的response对象

rep=requests.request()
rep.txt返回字符串的数据
rep.content返回字节格式的数据
rep.json()返回json格式的数据
rep.status_code返回状态码
rep.reason返回状态信息
rep.cookies返回cookie信息
rep.encoding返回编码信息
rep.headers返回响应头信息
rep.request.xxxxxx返回请求的信息

九、提取响应值

1、正则提取

# re.search只匹配一个值,通过下标[1]取值,没有匹配到返回None
token = re.search('"token":"(.*?)",',res.text)[1]
print("token1:%s"%token)

# re.findall匹配多个值,返回列表list,没有匹配到返回None
token = re.findall('"token":"(.*?)",',res.text)
print("token2:%s"%token)

2、jsonpath提取

# jsonpath.jsonpath返回的是一个列表,没有找到返回None
token = jsonpath.jsonpath(res.json(), '$.data.token')
print(token)

3、字典取值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值