一篇文章带你了解Python常用自动化测试框架——Pytest!

在之前的文章里我们已经学习了Python自带测试框架UnitTest,但是UnitTest具有一定的局限性

这篇文章里我们来学习第三方框架Pytest,它在保留了UnitTest框架语法的基础上有着更多的优化处理

下面我们将从以下角度来介绍Pytest:

Pytest基本介绍
Pytest基本使用
Pytest进阶内容
Pytest基本介绍
下面我们首先来简单介绍Pytest及相关内容

单元测试框架

我们首先需要知道测试一般分为四个方面的测试:

单元测试:称模块测试,针对软件设计中的最小单位——程序模块,进行正确性检查的测试工作

集成测试:称组装测试,通常在单元测试的基础上,将所有程序模块进行有序的、递增测试,重点测试不同模块的接口部分

系统测试:将整个软件系统看成一个整体进行测试,包括对功能、性能以及软件所运行的软硬件环境进行测试

验收测试:指按照项目任务书或合同、供需双方约定的验收依据文档进行的对整个系统的测试与评审,决定是否接收或拒收系统

而我们这篇文章主要针对的是单元测试:

Python:通常使用UnitTest和Pytest来进行单元测试自动化,但Pytest已经成为主流

Java:通常使用Testng和Junit来进行单元测试自动化,但Testng已经成为主流

最后我们需要明白单元测试框架的主要功能:

发现测试用例

执行测试用例

判断测试结果

生成测试报告

框架基本介绍

下面我们来简单介绍Pytest框架:

pytest是一个非常成熟的单元测试框架,经过多版本的迭代,主要优点在于灵活和简单

pytest具有极强的兼容性和生态环境,它可以结合selenium,requests,appium完成各种不同的自动化

pytest具有更好的页面展示效果,它可以生成自定义allure报告以及和Jenkins持续集成

下面我们给出一些和Pytest框架可以很好聚合的框架类型:

pytestpytest-html:主要用来生成html报告的插件

pytest-xdist:主要用来进行多线程运行的插件

pytest-ordering:主要用来改变用例的执行顺序的插件

pytest-rerunfailres:主要用来失败用例重跑的插件

allure-pytest:主要用来生成美观自定义的allure报告

我们可以采用一种比较简便的方式来一次性下载这些框架:

# 首先我们需要将这些名称全部放入一个txt文件中,假设我们放在requestment.txt文件中

# requestment.txt文件
pytest-html
pytest-xdist
pytest-ordering
pytest-rerunfailures

# 我们只需要在pycharm的console中输入指令下载该文件夹中全部内容即可
pip install -r requirements.txt

Pytest基本使用

下面我们来介绍Pytest的基本使用

Pytest默认测试用例

下面我们首先讲解Pytest默认测试用例的格式:

# 首先我们的模块名(文件名)通常被统一存放在一个testcases文件夹中,然后需要保证模块名须以test_开头或者_test结尾
# 例如我们下面的模块名命名就是正确示例
test_demo1
demo2_test

# 然后我们需要注意我们模块中的测试类类名必须以Test开头,并且不能带有init方法
# 例如我们下面的类名命名就是正确示例
class TestDemo1:
class TestLogin:

# 最后我们需要注意我们测试类中的测试方法名(Case名)必须以test_开头
# 例如我们下面的模块名命名就是正确示例
test_demo1(self)
test_demo2(self)

# 我们给出一个测试用例例子:
# 文件名为test_demo1
class TestDemo:
    def test_demo1(self):
        print("测试用例1")

    def test_demo2(self):
        print("测试用例2")
        
# 当然我们上述的要求都不是必须相同的,在后续我们可以进行修改,我们将在下述讲解执行方法时讲解

然后我们再来讲解一下Pytest的测试用例该如何执行:

# 首先我们讲解一下全局配置文件pytest.ini
# 我们可以在pytest.ini中进行一些属性的配置来修改Pytest的默认属性,我们需要在项目的根目录下创建,名称必须是pytest.ini

1 [pytest]
2 #参数
3 addopts = ‐vs 				# 这里指当默认使用指令时的一些辅助参数,我们后面会讲解
4 testpaths = ./testcases		# 这里指默认的执行路径,它会默认执行该文件夹下所有的满足条件的测试case
5 python_files = test_*.py		# 这里就是前面我们所说的文件命名规则
6 python_classes = Test*		# 这里就是前面我们所说的类名命名规则
7 python_functions = test_*		# 这里就是前面我们所说的Case命名规则
8 #标记
9 markers =						# 这里是冒烟规则,我们后面会讲到
10 smoke:冒烟用例				 
11 product_manage:商品管理		


# 然后我们首先来讲采用console命令行执行Pytest的方法
# 最简单的就是直接在console命令行输入pytest,如果存在pytest.ini,它会根据文件内容进行执行;如果没有就按照默认格式执行
# 但是我们可以通过一些参数来强化pytest参数指令

# -vs: -v输出详细信息 -s输出调试信息
pytest -vs

# -n: 多线程运行(前提安装插件:pytest-xdist)
pytest -vs -n=2

# --reruns num: 失败重跑(前提安装插件:pytest-rerunfailres)
pytest -vs --reruns=2

# -x: 出现一个用例失败则停止测试
pytest -vs -x

# --maxfail: 出现几个失败才终止
pytest -vs --maxfail=2

# --html: 生成html的测试报告,后面 需要跟上所创建的文件位置及文件名称(前提安装插件:pytest-html)
pytest -vs --html ./reports/result.html

# -k: 运行测试用例名称中包含某个字符串的测试用例,我们可以采用or表示或者,采用and表示都
# 采用or就表示:我们的运行用例名称中包含or两侧的其中一个数据即可
# 采用and就表示:我们的运行用例名称中包含and两侧的所有数据才满足条件
pytest -vs -k "qiuluo"
pytest -vs -k "qiuluo or weiliang"
pytest -vs -k "qiuluo and weiliang"

# -m:冒烟用例执行,后面需要跟一个冒烟名称
# 我们在这里简单介绍一下冒烟用例的执行方法,我们这里其实就是一个分组执行的方法
# 例如我们的用例划分为user_manage用户管理测试和product_manage商品管理测试,我们只希望执行其中一组测试

# 首先我们需要在他们的不同方法上进行@mark划分,具体操作如下:
class TestDemo:
    
    # 我们在Case上采用@pytest.mark. + 分组名称,就相当于该方法被划分为该分组中
    # 注意:一个分组可以有多个方法,一个方法也可以被划分到多个分组中
    @pytest.mark.user_manage
    def test_demo1(self):
        print("user_manage_test1")

    @pytest.mark.product_manage
    def test_demo2(self):
        print("product_manage_test1")
     
    @pytest.mark.user_manage
    @pytest.mark.product_manage
    def test_demo3(self):
        print("manage_test1")

# 我们在执行中只需要采用前面我们所说的-m + 分组名称即可
pytest -vs -m user_manage

# 这里插一句,我们在运行过程中可以采用抛出异常的方式来模拟测试失败:raise Exception() 抛出异常


# 最后我们也可以采用main方法来执行pytest,同样我们也可以使用参数来进行调节
if __name__ == '__main__':
	pytest.main()
    
if __name__ == '__main__':
	pytest.main(["‐vs"])

最后我们插入一个简单的案例跳过方法:

# pytest的跳过案例方法其实和unittest是完全相同的
# 我们只需要采用skip或skipif方法来指定参数并贴在方法上即可跳过

# @pytest.mark.skip(跳过原因)

# @pytest.mark.skipif(跳过条件,跳过原因)

# 我们给出一个示例
class TestDemo:
    
    workage2 = 5
    workage3 = 20
    
    @pytest.mark.skip(reason="无理由跳过")
    def test_demo1(self):
        print("我被跳过了")

    @pytest.mark.skipif(workage2<10,reason="工作经验少于10年跳过")    
    def test_demo2(self):
        print("由于经验不足,我被跳过了")
    
    @pytest.mark.skipif(workage3<10,reason="工作经验少于10年跳过")
    def test_demo3(self):
        print("由于经验过关,我被执行了")
        
    def test_demo3(self):
        print("我没有跳过条件,所以我被执行了")

Pytest前后置方法

首先我们需要先了解前后置是什么:

  • 前后置就是针对不同层级方法执行前和执行后所需要执行的步骤进行封装并执行

  • 这个层级通常被划分为:文件层,类层,方法层

首先我们先来介绍Pytest通过固件来实现前后置的方法:

# 我们通常采用前后置来做一些方法前后的操作
# 如果我们采用方法层的前后置,那么它会在每个方法执行前后去执行该内容
# 如果我们采用类层的前后置,那么它会在调用这个类内所有方法的前后去执行该内容,但是无论该类的方法执行多少次,它只会调用一次
# 例如我们做login测试时,我们只需要在开始测试时打开一次浏览器,然后在测试结束时关闭一次浏览器,那么我们就采用类的前后置
# 我们做login测试时,为了保证前置操作不对后续Case有影响,所以我们在执行方法前打开该网页,执行方法后关闭该网页,采用方法的前后置

# Pytest的固件前后置其实和unittest是基本相同的
# 首先是方法级别的固件前后置
# 它是在每个测试方法(用例代码) 执行前后都会自动调用的结构
# 方法执行之前
	def setUp(self):
		每个测试方法执行之前都会执行
		pass
# 方法执行之后
	def tearDown(self):
		每个测试方法执行之后都会执行
		pass
    
# 然后是针对类级别的固件前后置
# 它是在每个测试类中所有方法执行前后 都会自动调用的结构(在整个类中执行之前或之后执行一次)
# 需要注意:类级别的固件前后置, 是一个类方法
# 类中所有方法之前
    @classmethod
    def setUpClass(cls):
    	pass
# 类中所有方法之后
	@classmethod
	def tearDownClass(cls):
		pass
    
# 最后是针对模块级别的固件前后置
# 在每个代码文件执行前后执行的代码结构
# 需要注意:模块级别的需要写在类的外边直接定义函数即可
# 代码文件之前
	def setUpModule():
		pass
# 代码文件之后
	def tearDownModule():
		pass
    
# 下面我们采用一个用户账户登录的用例来简单展示一下固件前后置

import unittest

class TestLogin(unittest.TestCase):
    
    # 在执行该类前所需要调用的方法
    @classmethod
    def setUpClass(cls) -> None:
    	print('------打开浏览器')
    
    # 在执行该类后所需要调用的方法
    @classmethod
    def tearDownClass(cls) -> None:
    	print('------关闭浏览器')
    
    # 每个测试方法执行之前都会先调用的方法
    def setUp(self):
    	print('输入网址......')
    
    # 每个测试方法执行之后都会调用的方法
    def tearDown(self) -> None:
    	print('关闭当前页面......')
    
    # 测试Case1
    def test_1(self):
    	print('输入正确用户名密码验证码,点击登录 1')
  
	# 测试Case2
    def test_2(self):
    	print('输入错误用户名密码验证码,点击登录 2')

然后我们还需要讲解一下Fixtrue实现前后置的方法:

# 首先我们需要知道Fixtrue所实现的功能基本和固件所实现的功能是一样的,但是会更加方便
# 首先我们给出Fixture的完整格式,然后我们再分开介绍各个参数
@pytest.fixture(scope=None,autouse=False,params=None,ids=None ,name=None)

# scope:作用范围
# 参数主要有三种:function函数,class类,package/session包

# function:在函数层面上执行前后置
# 我们通常采用yield进行前后置划分,yield前是前置,yield后是后置
	@pytest.fixture(scope="function")
	def exe_database_sql():
		print("执行SQL查询")
		yield
		print("关闭数据库连接")
# 我们还可以通过yield或return去返回一些参数在方法中使用
# 但是需要注意,yield返回参数后后置仍旧可以执行,但是return返回参数后后置操作无法执行
	@pytest.fixture(scope="function")
	def exe_database_sql():
		print("执行SQL查询")
		yield "success"
      # return "success" 执行后无法执行后置操作
		print("关闭数据库连接")
# 我们的方法在调用时,可以直接使用exe_database_sql表示返回信息进行输出
def test_2(self,exe_database_sql):
    	print(exe_database_sql)
        
# class:在类之前和之后执行
	@pytest.fixture(scope="class")
	def exe_database_sql():
		print("执行SQL查询")
		yield
		print("关闭数据库连接")
        
# package/session:在整个项目会话之前和之后执行
	@pytest.fixture(scope="session")
	def exe_database_sql():
		print("执行SQL查询")
		yield
		print("关闭数据库连接")

        
# autouse:是否自动启动
# 该参数默认为False,我们可以将其修改为True
# 该参数的功能主要在判断该固件是否在自定义范围内可以自动启动
# 若自动启动,则所有方法在执行时都会自动执行该前后置;但若为False,则我们需要手动启动

# 首先如果是自动启动,则我们无需关心任何参数,我们的所有方法都会自动调用
	@pytest.fixture(scope="function",autoues=True)
	def exe_database_sql():
		print("执行SQL查询")
		yield
		print("关闭数据库连接")
        
# 但若是关闭自动启动,我们在不同的scope下有不同的调用方法
	@pytest.fixture(scope="function",autoues=Flase)
	def exe_database_sql():
		print("执行SQL查询")
		yield
		print("关闭数据库连接")
        
# scope = function:我们需要在方法后加上该Fixture方法名
	def test_2(self,exe_database_sql):
    	print(exe_database_sql)
        
# scope = class:我们需要在对应的类上添加@pytest.mark.usefixtures("exe_database_sql")装饰器调用
@pytest.mark.usefixtures("exe_database_sql")
class TestDemo:
    pass

# scope = session:.一般会结合conftest.py文件来实现,我们后面再介绍

# 还需要注意autouse仅限于在自己的类中使用上述方法,如果要跨类使用,那么我们也需要在conftest.py中配置


# params:实现参数化配置
# 通常我们的脚本都是根据导出的yaml文件进行属性填充,针对参数化我们后面再讲,我们先将Fixture的参数化
# params通常后面跟上具体的数据(列表,元组等),然后我们在调用时有固定的写法
# 首先我们需要在Fixture方法参数中定义一个request,然后使用request.param来使用我们传递的params数据
class TestDemo:
    def read_yaml():
        return ["胡桃","胡桃宝宝","胡桃厨"]
    
    # 首先我们的参数需要获取数据:params=read_yaml()
    @pytest.fixture(scope="function",autouse=False,params=read_yaml())
    # 然后我们的Fixture方法需要一个request参数
	def exe_database_sql(request):
        print("执行SQL查询")
        # 我们通过request.param获取数据,可以采用yield返回该数据
        yield request.param
        print("关闭数据库连接")
        

# ids:参数别名id
# 不能单独使用,必须和params一起使用,作用是对参数起别名 
# 我们在采用pytest进行测试数据输出时会有对应的方法调用n次,该n次采用不同的params参数,这个ids就是修改了console控制台展示数据
class TestDemo:
    def read_yaml():
        return ["胡桃","胡桃宝宝","胡桃厨"]
    
    # 当我们书写了ids,我们的控制输出就不会再是上面的["胡桃","胡桃宝宝","胡桃厨"],而是我们所书写的["1","2","3"]
    @pytest.fixture(scope="function",autouse=False,params=read_yaml(),ids=["1","2","3"])
	def exe_database_sql(request):
        print("执行SQL查询")
        # 我们通过request.param获取数据,可以采用yield返回该数据
        yield request.param
        print("关闭数据库连接")
        
# name:Fixture别名
# 作用是给fixtrue起别名,一旦使用了别名,那么fixtrue的名称就不能再用了,只能用别名
class TestDemo:
    
    # 如果我们在这里使用到了别名
    @pytest.fixture(scope="function",name="exe_datebase_sql_name")
	def exe_database_sql(request):
        print("执行SQL查询")
        yield 
        print("关闭数据库连接")
        
    # 我们这里就需要使用别名进行操作,之前的名称无法使用
	def test_2(self,exe_datebase_sql_name):
    	print(exe_database_sql)    

接下来我们就将会讲解到我们刚刚提到的conftest.py文件:

# 首先我们需要知道conftest.py文件的名字是固定形式,不可改变
# conftest.py文件主要就是用来存储我们的Fixture,然后我们会根据该文件的不同位置来判断可以使用的方法
# conftest可以在不同的目录级别下创建,如果我们在根目录下创建,那么所有case都会使用到该Fixture
# 但是如果我们在testcases文件夹下的某个模块文件下创建conftest.py,那么它的作用范围就只包含在该目录下

# 根目录创建的conftest.py
# 我们在该目录下的conftest文件里写的所有fixture可以在任意测试类下执行
import pytest
@pytest.fixture(scope="function",name="exe_datebase_sql_name")
def exe_database_sql():
    print("全部方法运行前均可以执行")
    yield 
    print("全部方法运行后均可以执行")
# testcases文件下的所有测试类
# 这里需要注意:我们使用conftest下的Fixture时,不需要import导包就可以使用
import pytest
class TestDemo1:
	# 测试Case1
    def test_1(self,exe_datebase_sql_name):
    	print('输入正确用户名密码验证码,点击登录 1' + exe_datebase_sql_name)
        
# testcases文件夹下的usercases文件夹下创建的conftest.py
# 我们在该目录下创建的conftest文件里写的所有fixture仅可以在该目录下的测试类中使用,在其他测试类中使用会出现报错
import pytest
@pytest.fixture(scope="function",name="usercases_fixture")
def exe_database_sql():
    print("usercases方法运行前均可以执行")
    yield 
    print("usercases方法运行后均可以执行")
# testcases文件下的usercases文件夹下的测试类
import pytest
class TestUserCases1:
	# 测试Case1
    def test_1(self,usercases_fixture):
    	print('输入正确用户名密码验证码,点击登录 1' + usercases_fixture)
        
# 最后我们简单给出一个前后置执行顺序优先级:
fixture_session > fixture_class > setup_class > fixture_function > setup

然后最后我们给出前后置执行的一个总体逻辑顺序:

查询当前目录下的conftest.py文件

查询当前目录下的pytest.ini文件并找到测试用例的位置

查询用例目录下的conftest.py文件

查询测试用例的py文件中是否有setup,teardown,setup_class,teardown_class

再根据pytest.ini文件的测试用例的规则去查找用例并执行

Pytest进阶内容
最后我们再来讲解一些pytest比较关键性的一些进阶内容

Allure效果美化

我们在使用Pytest所生成的页面往往不够美观且展示信息杂乱不好分析,所以我们通常搭载allure来实现界面美化:

Allure框架是一个灵活轻量级多语言测试报告工具

它不仅可以以WEB的方式展示简介的测试结果,而且允许参与开发过程的每个人从日常执行的测试中最大限度的提取有用信息

下面我们就来学习如何安装使用allure:
 

# 首先我们需要去下载在电脑上下载allure并配置好环境变量
# 我们这里给出官网下载地址:https://github.com/allure-framework/allure2/releases 
# 温馨提醒:下载链接在github上,如果无法打开可以刷新重试或者使用加速器梯子等辅助工具
# 环境变量的配置只需要将bin文件所在目录放在电脑的Path路径下即可,这里不再展示

# 第二步我们需要在pycharm上下载allure-pytest插件(如果之前pip了那个整体文件,这里应该是已经下载过了)
pip install allure-pytest

# 第三步我们就可以直接来生成allure的测试结果展示界面了

# 1.我们通常首先需要生成一个allure临时Json文件
# 我们通常会加上这么一串"‐‐alluredir=./temps ‐‐clean‐alluredir"
# ‐‐alluredir = 文件生成地址 : 表示我们将allure临时文件生成在我们所指定的相对临时目录下
# ‐‐clean‐alluredir : 由于每次都会生成大量文件,所以我们会在生成前清除当前目录下的allure文件,保证我们数据都是最新数据

# 2.我们需要依靠临时文件来生成allure.html网页
# 我们通常在main方法中执行
if __name__ == '__main__':
    # 正常运行
    pytest.main()
    # 休眠:主要为了JSON临时文件的生成
    time.sleep(3)
    # allure generate 固定语句 + allure临时JSON文件目录 + -o 输出指令 + allure.html生成文件目录 + --clean 清除旧数据
    os.system("allure generate ./temps ‐o ./reports ‐‐clean")

Parametrize数据驱动

我们通常会采用Parametrize注解来进行数据驱动,下面我们来详细讲解一下:

# 格式:@pytest.mark.parametrize(参数名称,参数值)
# 意义:我们会将参数名称作为id,然后根据参数值的个数去依次调用,存在n个参数值,我们将会调用n次case

# 1.参数值为列表或元组时,参数名称可以为一个
# 首先我们这里因为使用单个元素的列表(元组),我们的参数名可以为一个
@pytest.mark.parametrize('caseinfo',['胡桃','胡桃宝宝','芙芙','芙芙宝宝'])
# 在方法参数里,我们需要调用parametrize的参数名称caseinfo,需要保证一模一样
def test_01_get_token(self,caseinfo):
    # 在这里我们可以借助参数名称caseinfo来代替列表中的元素
    # 列表中存在几个,我们该方法将执行几次,例如现在列表是四个元素,那么我们方法将会重复执行四次并每次按顺序赋值不同的元素
	print("获取统一接口鉴权码:"+caseinfo)
    
# 2.参数值为列表的多个时,参数名称可以为多个
# 这里我们列表中嵌套了一个列表,如果我们是单参数名称,那么输出时就会将第一个列表['胡桃厨','胡桃宝宝']输出出去
# 但是如果我们是多参数名称,系统会自动将第一个列表的元素分开赋值给arg1,arg2便于我们分开使用,个人还是比较推荐的
@pytest.mark.parametrize('arg1,arg2',[['胡桃厨','胡桃宝宝'],['芙芙厨','芙芙宝宝']])
# 注意:这里当然也需要和参数名称对应!!!
def test_01_get_token(self,arg1,arg2):
	print("获取统一接口鉴权码:"+str(arg1)+" "+str(arg2))

我们在进行数据驱动时通常会结合Yaml文件来进行数据获取,这里我们简单介绍一下Yaml文件:

# yaml是一种数据格式,扩展名可以是yaml,yml
# 支持#注释,通过缩进表示层级,区分大小写,且yaml文件最后获取的结果展示是一个字典列表格式
# yaml文件经常用于书写配置,例如Java的Spring中的配置文件,而我们也经常采用yaml编写自动化测试用例

# yaml文件通常会出现两种格式

# 字典格式:如果我们正常书写yaml文件,如下就是字典模式
name: 胡桃

# 列表模式:如果我们采用yaml中的列表,那么我们在py获取时也将获得列表
msjy:
    - name1: 胡桃
    - name2: 芙芙
    - ages1: 18
    - ages2: 19
# 我们也可以利用这个特性,直接在yaml中做多个列表,来多次提取
-
	name:'xxx'
    age:18
-
	name:'xxx'
    age:20

# 我们这里首先给出一个解析yaml文件的示例函数:
import os.path
import yaml

# 这里是获取当前路径,因为我们需要找到对应的yaml文件,那么具体路径就需要我们进行拼接
def get_obj_path():
    # 这里我们使用了Python的os类来进行当前路径获取,最后返回结果其实是一个String字符串
    # 我们以'common'作为分界(common是当前文件夹的名称,我们将该Str进行划分,获取前面的部分),获取到前面的路径部分来进行拼接
    return os.path.dirname(__file__).split('common')[0]

# 然后我们这里定义一个方法来解析yaml文件
def read_yaml(yamlPath):
    with open(get_obj_path() + yamlPath,mode = 'r',encoding = 'utf-8') as f:
        # 这里需要我们pip install pyyaml
        value = yaml.load(steam=f,Loader=yaml.FullLoader)
        return value
    
# 然后我们这里采用一个main方法来执行上述用例(其实应该在其他测试类中执行)
if __name__ = '__main__':
    # 调用read_yaml方法并给出yaml路径
    print(read_yaml('testcase/user_manage/get_token.yaml'))

# 了解了所有东西之前我们就可以结合之前的Parametrize来进行操作:
# 我们这里将所需要的数据变为read_yaml读取的yaml文件内容
@pytest.mark.parametrize('caseinfo',read_yaml('testcase/user_manage/get_token.yaml'))
def test_01_get_token(self,caseinfo):
    # 这里我们就可以获取到yaml文件内容并输出了
	print("获取统一接口鉴权码:"+caseinfo)
    
# 当然如果我们了解我们的yaml中拥有什么元素,我们还可以采用[]的方式具体表达出来
@pytest.mark.parametrize('caseinfo',read_yaml('testcase/user_manage/get_token.yaml'))
def test_01_get_token(self,caseinfo):
	print("获取统一接口鉴权码:")
    # 这里我们可以直接获取namekey对应的value
    print("caseinfo[name]:"+ caseinfo['name'])
    # 这里我们可以分别获取request层下的method,url,data分别对应的value
    print("caseinfo[name]:"+ caseinfo['request']['method'])
    print("caseinfo[name]:"+ caseinfo['request']['url'])
    print("caseinfo[name]:"+ caseinfo['request']['data'])

链接:https://www.cnblogs.com/qiuluoyuweiliang/p/17828833.html

最后感谢每一个认真阅读我文章的人,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

文档获取方式:点击右边链接领取:软件测试全套资料分享      

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/314973.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[ctf.show 元旦水友赛 2024] crypto

感觉半个多月回家没有打开过电脑了。看到ctf.show上元旦的比赛&#xff0c;才想起似乎应该看看。 月月的爱情故事 上来这就是个小脑洞题&#xff0c;给了一大段文字和一个base64的串。并且提示&#xff1a;试试摩斯吧&#xff01; 从文字上看只有三种标点符号&#xff0c;显…

【书生·浦语大模型实战营04】《(4)XTuner 大模型单卡低成本微调实战》学习笔记

《(4)XTuner 大模型单卡低成本微调实战》 课程文档&#xff1a;《XTuner 大模型单卡低成本微调实战》 1 Finetune简介 LLM的下游应用中&#xff0c;增量预训练和指令跟随是经常会用到两种的微调模式 1.1 增量预训练微调 使用场景&#xff1a;让基座模型学习到一些新知识&a…

蓝桥杯单片机组备赛——蜂鸣器和继电器的基本控制

文章目录 一、蜂鸣器和继电器电路介绍二、题目与答案2.1 题目2.2 答案2.3 重点函数解析 一、蜂鸣器和继电器电路介绍 可以发现两个电路一端都接着VCC&#xff0c;所以我们只要给另一端接上低电平就可以让蜂鸣器和继电器进行工作。与操作LED类似&#xff0c;只不过换了一个74HC5…

JWT---JSON Web Token

JSON Web Token是什么 JSON Web Token (JWT)是一个开放标准(RFC 7519)&#xff0c;它定义了一种紧凑的、自包含的方式&#xff0c;用于作为JSON对象在各方之间安全地传输信息。该信息可以被验证和信任&#xff0c;因为它是数字签名的。 JSON Web Token的结构是什么样的 JSON…

DynastyPersist:一款功能强大的Linux持久化安全审计与测试工具

关于DynastyPersist DynastyPersist是一款专为红队研究人员和CTF玩家设计的Linux安全测试工具&#xff0c;该工具可以适用于各种安全评估任务和安全测试场景。 DynastyPersist本质上是一个Linux持久化脚本&#xff0c;并提供了大量的安全测试功能&#xff0c;可以为我们展示在…

问界又“翻车”了? 新能源电池“怕冷”成短板

文 | AUTO芯球 作者 | 李欣 2023年12月17日&#xff0c;蔚来创始人李斌亲自下场&#xff01;驾驶ET7从上海出发&#xff0c;经过超14小时的行驶后&#xff0c;达成一块电池行驶超过1000公里的成绩&#xff0c;这一直播引起外界的广泛关注。 这不禁让人与”懂车帝冬测“联想到…

布隆过滤器 Bloom Filter

文章目录 1、什么是布隆过滤器&#xff1f;1.1 工作原理1.2 布隆过滤器的优点1.3 缺点 2、布隆过滤器的使用场景3、布隆过滤器的原理3.1 布隆过滤器的数据结构3.2 初始化阶3.3 插入元素过程3.4 查询元素是否存在3.5 元素删除3.6 扩容 4、SpringBoot 整合 布隆过滤器4.1 技术选型…

书生·浦语大模型--第二节课笔记

书生浦语大模型--第二节课 大模型及InternLM基本介绍实战部分demo部署准备工作模型下载代码准备终端运行web demo 运行 Lagent 智能体工具调用 Demo准备工作Demo 运行 浦语灵笔图文理解创作 Demo环境准备下载模型下载代码运行 大模型及InternLM基本介绍 大模型 定义&#xff…

【排序算法】插入排序与希尔排序,你不想知道为什么希尔比插入更快吗?

文章目录 &#x1f680;前言&#x1f680;插入排序&#xff08;insertsort&#xff09;✈️原理✈️代码实现&#xff08;coding&#xff09; &#x1f680;总结&#x1f680;希尔排序&#xff08;shellsort&#xff09;✈️代码实现&#xff08;coding&#xff09;✈️为啥希尔…

Python web自动化测试框架搭建(功能接口)——通用模块

1、通用模块&#xff1a; config.conf: 公共配置文件&#xff0c;配置报告、日志、截图路径&#xff0c;以及邮件相关配置 [report] reportpath E:\workspace\WebAutomation\src\functiontest\Report\2017-07-18 screen_path E:\workspace\WebAutomation\src\functiontest\R…

Pygame程序的屏幕显示

不同对象的绘制与显示过程 在Pygame中&#xff0c;需要将所有需要在屏幕上显示的内容都绘制在一个display surface上。该Surface通常称为screen surface&#xff0c;它是pygame.display.set_mode()函数返回的Surface对象。 在绘制不同对象时&#xff0c;可以使用不同的绘制方…

Linux - No space left on device

问题描述 No space left on device 原因分析 说明在服务器设备上的存储空间已经满了&#xff0c;不能再上传或者新建文件夹或者文件等。 解决方案 确认查看服务器系统的磁盘使用情况是否是真的已经没有剩余空间&#xff0c;复制下面命令在服务器上运行&#xff0c;然后发现如果…

用友U8 BI数据可视化报表怎么做?秘籍在这!

首先要找到一款顺利对接用友U8的BI数据可视化分析工具&#xff0c;简称BI工具、BI软件。这款BI工具需符合以下要求&#xff1a; 1、能对接用友U8系统。 2、有专门根据用友系统特性量身打造的标准化BI方案&#xff0c;也就是有标准化的用友U8 BI方案。 3、数据可视化图表丰富…

有没有游泳可以戴的耳机?2024年高性价比游泳耳机推荐

科技的不断进步为我们的生活带来了更多的便利和乐趣&#xff0c;游泳耳机作为一种专门设计用于水中活动的耳机也在不断演进。在畅游的时候&#xff0c;能够携带一款高性价比的游泳耳机&#xff0c;不仅可以让您更好地享受音乐&#xff0c;还能为游泳时的独特体验增色不少。 因…

HarmonyOS——ArkUI状态管理

一、状态管理 在声明式UI编程框架中&#xff0c;UI是程序状态的运行结果&#xff0c;用户构建了一个UI模型&#xff0c;其中应用的运行时的状态是参数。当参数改变时&#xff0c;UI作为返回结果&#xff0c;也将进行对应的改变。这些运行时的状态变化所带来的UI的重新渲染&…

ES索引原理

ES在检索时底层使用的就是倒排索引&#xff0c;正向索引是通过key找value&#xff0c;反向索引则是通过value找key。 索引会分为两个区域&#xff1a;索引区和元数据区。数据是这样存储在里面的&#xff1a; 简单理解就是&#xff1a;当要录入一条数据时&#xff0c;首先会将完…

红黑树(RBTree)

目录​​​​​​​ 一、红黑树简介 二、红黑树的来源 三、什么是红黑树 四、红黑树的性质 五、红黑树的节点定义 六、红黑树的操作 6.1、红黑树的查找 6.2、红黑树的插入 七、红黑树的验证 八、红黑树和AVL树的比较 一、红黑树简介 红黑树是一种自平衡的二叉查找树…

C++内存管理机制(侯捷)笔记4(完结)

C内存管理机制&#xff08;侯捷&#xff09; 本文是学习笔记&#xff0c;仅供个人学习使用。如有侵权&#xff0c;请联系删除。 参考链接 Youtube: 侯捷-C内存管理机制 Github课程视频、PPT和源代码: https://github.com/ZachL1/Bilibili-plus 介绍 下面是第四讲和第五讲…

02. 坦克大战项目-准备工作和绘制坦克

02. 坦克大战项目-准备工作和绘制坦克 01. 准备工作 1. 首先我们要创建四个类 1. Tank类 介绍&#xff1a;Tank 类主要用来表示坦克的基本属性和行为 public class Tank {private int x;//坦克的横坐标private int y;//坦克的纵坐标public int getX() {return x;}public v…

HTML 链接 图片引入

文章目录 链接图片引入 链接 准备工作 新建一个名为link.html和suc.html suc.html <!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>显示结果</title></head><body>注册成功...&l…