2024全网最火的接口自动化测试,一看就会

 🔥 交流讨论:欢迎加入我们一起学习!

🔥 资源分享耗时200+小时精选的「软件测试」资料包

🔥 教程推荐:火遍全网的《软件测试》教程  

📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

目录

  1. 接口自动化测试用例设计
  2. Excel接口测试用例访问
  3. MySQL接口测试用例访问
  4. PyTest测试框架
  5. 接口自动化测试必备技能-HTTP协议
  6. request库实现接口请求

引言

与UI相比,接口一旦研发完成,通常变更或重构的频率和幅度相对较小。因此做接口自动化的性价比更高,通常运用于迭代版本上线前的回归测试中。

手工做接口测试,测试数据和参数都可以由测试人员手动填写和更新。

因此我们在考虑将接口用例实现自动化的时候,主要思路就是在单个接口请求的测试用例已经完成的前提下,我们如何解决以下问题:

  1. 业务测试场景会调用不止一个接口,下一个接口的请求依赖于上一个接口的数据,需要解决接口依赖问题
  2. token等鉴权数据有过期时间,多个接口用到该参数,需要解决一次修改,多处生效的问题
  3. 一个接口要用到多个测试数据做覆盖
  4. 批量测试下,需要知道某个接口返回的参数/数据是否符合预期

接口自动化测试的基本流程

1、需求分析
2、挑选需要做自动化测试的功能接口
3、设计测试用例
4、搭建我们的自动化测试环境
5、设计自动化测试项目的整体架构
6、编写脚本代码
7、运行测试用例脚本
8、生成自动化测试报告

第一章. 接口自动化测试用例设计

1. 接口测试的用例设计思路
  • 思考:接口测试,测试的目的是什么?
  • 根据接口的参数,设计输入数据,验证接口的返回值
  • 所以,其测试范围就是围绕着参数进行用例的设计
  • 就是准备各种各样的数据,对接口的返回值进行验证
  • 思路: 正确的值 错误的值 异常的值 边界的值 根据场景设计的值 ......

最终,只是看不见页面的黑盒用例设计方法

但是,我们自己要尽量根据接口要产生画面感

2. 接口自动化测试用例设计要点

尽量让人和代码都能够看懂且用得上 但是,很多时候我们做不到那就只能分开

  • 接口自动化测试用例字段设计

用例编号、用例标题、请求接口类别、请求地址、输入数据(分字段)、数据格式、请求方式、是否需要登录、期望结果

  • 接口自动化实施的前提

接口已趋于稳定

3. 接口自动化测试用例书写

第二章. Excel接口测试用例访问

www.runoob.com/pandas/pand…

1. pandas库的安装及库安装方法总结

方法一:cmd命令行执行pip install pandas 1.Windows+R,输入cmd打开命令行窗口,输入pip install pandas。 下图所示

2.若出现下图所示的告警,说明版本有冲突。

按照提示输入pip install --upgrade pip,对pip进行升级

3.若出现下图所示的升级报错,输入python -m ensurepip,python -m pip install --upgrade pip

4.再输入pip install pandas进行安装

5.若依然安装报错,可采取方法二进行安装

方法二:找到pandas下载库,然后通过pip install [pandas包路径]安装 1.进入网站 www.lfd.uci.edu/~gohlke/pyt… Ctrl+F,输入pandas,找到需要的版本下载即可。

比如我要下载的是pandas-1.4.3-cp310-cp310-win_amd64.whl。 下载后保存到磁盘某一个路径下

2.打开cmd命令行,输入pip install [pandas包路径]

方法三:如果报超时的错,是因为国外的源文件在国内下载比较慢,可更换为国内的镜像。

使用清华大学的pandas镜像进行安装: pip install -i pypi.tuna.tsinghua.edu.cn/simple pandas

下载很快,且无报错。 最后执行一下python -m pip list命令查看已经安装的包,可以看到不光pandas已经装好了,还安装了numpy库。

  • 执行以下代码打印出第三章接口测试用例
 
  1. import pandas as pd

  2. # 获取excel中的数据

  3. sheet1_data = pd.read_excel("../第三章接口测试用例.xlsx")

  4. print(sheet1_data)

  5. 复制代码

  • 显示所有的列
 
  1. import pandas as pd

  2. # 显示所有的列

  3. pd .set_option('display.max_columns',None)

  4. # 显示所有的行

  5. pd .set_option('display.max_rows',None)

  6. # 获取excel中的数据

  7. sheet1_data = pd.read_excel("../第三章接口测试用例.xlsx")

  8. print(sheet1_data)

  9. 复制代码

2. Excel的数据访问

输入:sheet2_data = pd.read_excel("../第三章接口测试用例.xlsx"sheet_name="Sheet2") 后直接报以下图

 
  1. import pandas as pd

  2. # 显示所有的列

  3. pd .set_option('display.max_columns',None)

  4. # 显示所有的行

  5. pd .set_option('display.max_rows',None)

  6. # 获取excel中的数据

  7. sheet1_data = pd.read_excel("../第三章接口测试用例.xlsx")

  8. sheet2_data = pd.read_excel("../第三章接口测试用例.xlsx"sheet_name="Sheet2")

  9. print(sheet2_data)

  10. 复制代码

输入以下可访问到,但是也报错

 
  1. import pandas as pd

  2. # 显示所有的列

  3. pd .set_option('display.max_columns',None)

  4. # 显示所有的行

  5. pd .set_option('display.max_rows',None)

  6. # 获取excel中的数据

  7. sheet1_data = pd.read_excel("../第三章接口测试用例.xlsx")

  8. sheet2_data = pd.read_excel("../第三章接口测试用例.xlsx"sheet_name="Sheet2")

  9. # print(sheet2_data)

  10. print(sheet1_data)

  11. print(type(sheet1_data))

  12. 复制代码

单独访问一列的数据

 
  1. import pandas as pd

  2. # 显示所有的列

  3. pd .set_option('display.max_columns',None)

  4. # 显示所有的行

  5. pd .set_option('display.max_rows',None)

  6. # 获取excel中的数据

  7. sheet1_data = pd.read_excel("../第三章接口测试用例.xlsx")

  8. sheet2_data = pd.read_excel("../第三章接口测试用例.xlsx"sheet_name="Sheet2")

  9. # print(sheet2_data)

  10. print(sheet1_data)

  11. print(type(sheet1_data))

  12. # 单独访问一列的数据

  13. print("*"*20)

  14. print(sheet1_data['编号'])

  15. 复制代码

3. Python接口自动化Excel读取测试用例
 
  1. # 导包

  2. import xlrd

  3. def get_exceldate(filepath,sheet_name,casename):

  4. print(filepath)

  5. res_list = []

  6. # 获取指定的表对象

  7. work_book = xlrd.open_workbook(filepath,formatting_info=True)

  8. # 操作指定的表单

  9. work_sheet = work_book.sheet_by_name(sheet_name)

  10. count =0

  11. # 获取第0列全部数据作为遍历对象

  12. for one_col in work_sheet.col_values(0):

  13. # 防止列中有多余数据 进行判断

  14. if casename in one_col:

  15. #获取每行第九列的数据

  16. req_body = work_sheet.cell(count,9).value

  17. #获取每行第十一列的数据

  18. resp_exp = work_sheet.cell(count,11).value

  19. #将数据 加入到列表中

  20. res_list.append(req_body)

  21. #将数据 加入到列表中

  22. res_list.append(resp_exp)

  23. print(one_col)

  24. if __name__ == '__main__':

  25. get_exceldate("D:/testdata.xls","访问",'access')

  26. 复制代码

第三章.MySQL数据库接口测试用例访问

1.MySQL存储测试用例的优劣势
  1. 企业中测试团队工作场景
  • 多人共同测试同一个项目
  • 同事之间的工作有不同的分工和交叉
  1. Excel存储测试用例的优劣势
  • 优势:数据操作方便
  • 劣势:团队中数据不易同步,数据复用性差
  1. MySQL数据存储的优劣势
  • 优势:数据同步与共享方便,数据复用性强,用例留有痕迹
  • 劣势:有一定的技术门槛
2.MySQL测试用例管理思路
  1. 测试用例书写
  • 可以与Excel测试用例字段一致
  • 但是,字段名称要是用英文,这样更符合规范
  1. Excel测试用例数据导入
  • 点击表,右键进入导入向导

  • 进入之后可选择相应的文件(xlsx)

  • 导入从磁盘寻找想要的用例表格,随后再打开

  • 勾选必要用的

  • 输入字段名行,第一个数据行

  • 可以调整

  • 选择导入模式

  • 最后就是这样的

3.PyMySQL结合Pandas操作MySQL数据库
 
  1. import pymysql

  2. db_info = {

  3. "host":"192.168.0.108",

  4. "user":"root",

  5. "password":"mydb2",

  6. "charset":"utf8"

  7. }

  8. conn = pymysql.connect(**db_info)

  9. cursor = conn.cursor()

  10. sql = "select * from mumu"

  11. cursor.execute(sql)

  12. result = cursor.fetchall()

  13. print(result)

  14. 复制代码

  • 结合pandas执行sql
 
  1. import pymysql

  2. import pandas as pd

  3. db_info = {

  4. "host":"192.168.0.108",

  5. "user":"root",

  6. "password":"mydb2",

  7. "charset":"utf8"

  8. }

  9. conn = pymysql.connect(**db_info)

  10. # cursor = conn.cursor()

  11. # sql = "select * from mumu"

  12. # cursor.execute(sql)

  13. # result = cursor.fetchall()

  14. # print(result)

  15. # print(type(result)

  16. # 结合pandas执行sql

  17. sql = "select * from mumu"

  18. result = pd read_sql(sql,conn)

  19. print(result)

  20. 复制代码

第四章.PyTest自动化测试框架

1. PyTest介绍与安装
  1. PyTest介绍
  • PyTest是python的一个第三方的单元测试库
  • 自动识别测试模块和测试函数
  • 支持非常丰富的断言(assert)语句
  1. PyTest中的使用约束
  • 测试文件的文件名必须以"test_"或"_test"结尾
  • 测试类必须以“Test”开头
  • 测试的函数名必须以"_test"开头
  • 测试类里面不能使用"init"方法
  1. PyTest安装命令

pip install pytest

cd到这个文件

  • 输入1时打印结果为:
 
  1. import pytest

  2. def test_demo():

  3. assert 1 == 1

  4. if __name__ =="__main__":

  5. pytest.main(['test_pytest1.py'])

  6. 复制代码

  • 输入2时打印结果为:
 
  1. import pytest

  2. def test_demo():

  3. assert 1 == 1

  4. if __name__ =="__main__":

  5. pytest.main(['test_pytest1.py'])

  6. 复制代码

2. PyTest中的断言
  • 执行代码打开自己的文件
 
  1. import pytest

  2. def add(a,b):

  3. return a + b

  4. def test_dengyu():

  5. assert 3 == add(1,2)

  6. if __name__ == "__main__":

  7. pytest.main(['test_pytest2.py'])

  8. 复制代码

  • 执行以下代码:
 
  1. import pytest

  2. def add(a,b):

  3. return a + b

  4. def test_dengyu():

  5. assert 3 == add(1,2)

  6. def test_budengyu():

  7. assert 5 != add(1,3)

  8. def test_dayu():

  9. assert 5 > add(1,3)

  10. def test_dayudengyu():

  11. assert 5 >= add(1,3)

  12. def test_xiaoyu():

  13. assert 1 < add(1,2)

  14. def test_xiaoyudengyu():

  15. assert 1 <= add(1,3)

  16. def test_baohan():

  17. assert 1 in [1,2,3]

  18. def test_bubaohan():

  19. assert 1 not in [1,2,3]

  20. def test_iftrue():

  21. assert 1 is True

  22. def test_iffalse():

  23. assert 0 is False

  24. if __name__ == "__main__":

  25. pytest.main(['test_pytest2.py'])

  26. 复制代码

  • 打印结果为三个失败,七个通过
 
  1. D:\python\python.exe "D:/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5\test_pytest2.py

  2. Testing started at 18:45 ...

  3. Launching pytest with arguments C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5\test_pytest2.py --no-header --no-summary -q in C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5

  4. ============================= test session starts =============================

  5. collecting ... collected 10 items

  6. test_pytest2.py::test_dengyu PASSED [ 10%]

  7. test_pytest2.py::test_budengyu PASSED [ 20%]

  8. test_pytest2.py::test_dayu PASSED [ 30%]

  9. test_pytest2.py::test_dayudengyu PASSED [ 40%]

  10. test_pytest2.py::test_xiaoyu PASSED [ 50%]

  11. test_pytest2.py::test_xiaoyudengyu PASSED [ 60%]

  12. test_pytest2.py::test_baohan PASSED [ 70%]

  13. test_pytest2.py::test_bubaohan FAILED [ 80%]

  14. test_pytest2.py:26 (test_bubaohan)

  15. 1 != [1, 2, 3]

  16. Expected :[1, 2, 3]

  17. Actual :1

  18. <Click to see difference>

  19. def test_bubaohan():

  20. > assert 1 not in [1,2,3]

  21. E assert 1 not in [1, 2, 3]

  22. test_pytest2.py:28: AssertionError

  23. test_pytest2.py::test_iftrue FAILED [ 90%]

  24. test_pytest2.py:29 (test_iftrue)

  25. 1 != True

  26. Expected :True

  27. Actual :1

  28. <Click to see difference>

  29. def test_iftrue():

  30. > assert 1 is True

  31. E assert 1 is True

  32. test_pytest2.py:31: AssertionError

  33. test_pytest2.py::test_iffalse FAILED [100%]

  34. test_pytest2.py:32 (test_iffalse)

  35. 0 != False

  36. Expected :False

  37. Actual :0

  38. <Click to see difference>

  39. def test_iffalse():

  40. > assert 0 is False

  41. E assert 0 is False

  42. test_pytest2.py:34: AssertionError

  43. ========================= 3 failed, 7 passed in 0.21s =========================

  44. Process finished with exit code 1

  45. 复制代码

3. PyTest中的参数化
  • 执行以下代码三个通过,三个失败:
 
  1. # 参数化

  2. import pytest

  3. def add(a,b):

  4. return a + b

  5. #第一种实现参数化的写法

  6. @pytest.mark.parametrize(['x','y'],[(1,2),(0,3),(1,4)])

  7. def test_add1():

  8. assert 3 == add(x,y)

  9. # 第二种参数化的写法

  10. xy = [(-1,3),(-1,4),(1,-4)]

  11. @pytest.mark.parametrize(['x','y'],xy)

  12. def test_add2(x,y):

  13. assert 3 == add(x,y)

  14. if __name__ == "__main__":

  15. pytest.main(['test_pytest3.py'])

  16. 复制代码

 
  1. # 参数化

  2. import pytest

  3. def add(a,b):

  4. return a + b

  5. #第一种实现参数化的写法

  6. @pytest.mark.parametrize(['x','y'],[(1,2),(0,3),(1,4)])

  7. def test_add1():

  8. assert 3 == add(x,y)

  9. # 第二种参数化的写法

  10. xy = [(-1,3),(-1,4),(1,-4)]

  11. @pytest.mark.parametrize(['x','y'],xy)

  12. def test_add2(x,y):

  13. assert 3 == add(x,y)

  14. # 把存储在mysql当中的测试用例参数发出来

  15. import pymysql

  16. db_info={

  17. "host":"192.168.0.108",

  18. "user":"root",

  19. "database":"db2"

  20. "charset":"utf8"

  21. }

  22. conn pytest.connect(**db_info)

  23. cursor = conn.cursor()

  24. sql = "select * from mumu"

  25. cursor.execute(sql)

  26. result = cursor.fetchall()

  27. @pytest.mark.parametrize(['id',

  28. 'case_id',

  29. 'interface_type',

  30. 'uri',

  31. 'method',

  32. 'if_login',

  33. 'input_data',

  34. 'data_type',

  35. 'expect'],result)

  36. def test_mysql_parm(id,case_id,interface_type,uri,method,

  37. if_login, input_data,data_type,

  38. expect):

  39. print(title)

  40. if __name__ == "__main__":

  41. pytest.main(['test_pytest3.py'])

  42. 复制代码

4. PyTest中的组织管理
  • 执行以下打印结果为9个都通过
 
  1. # 测试用例的组织管理

  2. import pytest

  3. def test_01():

  4. print(1)

  5. def test_02():

  6. print(2)

  7. def test_03():

  8. print(3)

  9. def test_04():

  10. print(4)

  11. class TestLogin():

  12. def test_05(self):

  13. print(5)

  14. def test_06(self):

  15. print(6)

  16. def test_07(self):

  17. print(7)

  18. def test_08(self):

  19. print(8)

  20. def test_09(self):

  21. print(9)

  22. if __name__ == "__main__":

  23. pytest.main(['test_pytest4.py'])

  24. 复制代码

 
  1. D:\python\python.exe "D:/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5\test_pytest4.py

  2. Testing started at 19:25 ...

  3. Launching pytest with arguments C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5\test_pytest4.py --no-header --no-summary -q in C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5

  4. ============================= test session starts =============================

  5. collecting ... collected 9 items

  6. test_pytest4.py::test_01 PASSED [ 11%]1

  7. test_pytest4.py::test_02 PASSED [ 22%]2

  8. test_pytest4.py::test_03 PASSED [ 33%]3

  9. test_pytest4.py::test_04 PASSED [ 44%]4

  10. test_pytest4.py::TestLogin::test_05 PASSED [ 55%]5

  11. test_pytest4.py::TestLogin::test_06 PASSED [ 66%]6

  12. test_pytest4.py::TestLogin::test_07 PASSED [ 77%]7

  13. test_pytest4.py::TestLogin::test_08 PASSED [ 88%]8

  14. test_pytest4.py::TestLogin::test_09 PASSED [100%]9

  15. ============================== 9 passed in 0.06s ==============================

  16. Process finished with exit code 0

  17. 复制代码

 
  1. # 测试用例的组织管理

  2. import pytest

  3. def test_01():

  4. print(1)

  5. def test_02():

  6. print(2)

  7. def test_03():

  8. print(3)

  9. @pytest.mark.run(order=1)

  10. def test_04():

  11. print(4)

  12. class TestLogin():

  13. def test_05(self):

  14. print(5)

  15. def test_06(self):

  16. print(6)

  17. def test_07(self):

  18. print(7)

  19. def test_08(self):

  20. print(8)

  21. def test_09(self):

  22. print(9)

  23. if __name__ == "__main__":

  24. pytest.main(['test_pytest4.py'])

  25. """

  26. 在pytest当中,测试方法执行的顺序,默认是从上到下

  27. 我们使用pytest.mark.run进行测试函数执行顺序的标记时

  28. 需要先安装pip inst pytest_ordering

  29. """

  30. 复制代码

 
  1. D:\python\python.exe "D:/PyCharm Community Edition 2022.3.2/plugins/python-ce/helpers/pycharm/_jb_pytest_runner.py" --path C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5\test_pytest4.py

  2. Testing started at 19:33 ...

  3. Launching pytest with arguments C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5\test_pytest4.py --no-header --no-summary -q in C:\Users\Administrator\PycharmProjects\pythonProject\part2\chapter5

  4. ============================= test session starts =============================

  5. collecting ... collected 9 items

  6. test_pytest4.py::test_04 PASSED [ 11%]4

  7. test_pytest4.py::test_01 PASSED [ 22%]1

  8. test_pytest4.py::test_02 PASSED [ 33%]2

  9. test_pytest4.py::test_03 PASSED [ 44%]3

  10. test_pytest4.py::TestLogin::test_05 PASSED [ 55%]5

  11. test_pytest4.py::TestLogin::test_06 PASSED [ 66%]6

  12. test_pytest4.py::TestLogin::test_07 PASSED [ 77%]7

  13. test_pytest4.py::TestLogin::test_08 PASSED [ 88%]8

  14. test_pytest4.py::TestLogin::test_09 PASSED [100%]9

  15. ============================== 9 passed in 0.06s ==============================

  16. Process finished with exit code 0

  17. 复制代码

5. PyTest框架简介

  pytest,rf(学关键字语法,报告漂亮),unitest   pytest是python的第三方单元测试框架,可以做系统测试,比unitest更简洁和高效,执行315种以上的插件,

    同时兼容unittest框架,在unittest框架迁移到pytest框架的代码不需要重写代码   unittest框架迁移到pytest框架的时候不需要重写代码   纯python代码的自动化测试框架

1:Pytest框架简介:   接口测试方案:python

    一:工具类:纯手工测试,用工具来做(postman jemeter soapui)--入门简单,不好扩展(后面很多框架定制化)     二:代码类:现成的python框架:unitest(单元测试比较多,最原始的解释器自带的,不需要安装,不支持定制化,分布式) pytest(高级,效率高,支持定制化) nose       rf(报告篇评论,需要学会--封装关键字)       pytest和nose都是unitest扩展的更高级的一个库,框架,基于unitest     三:测试平台:现成平台,公司自己定制开发的,不对外 (融合jmeter,)  综合平台       前端       后端       执行机制----框架pytest(一般融合了禅道,框架,邮件各种功能)

  pytest是python的第三方单元测试框架,可以做系统测试,比unitest更简洁和高效,支持315种以上的插件,

  同时兼容unittest框架,在unittest框架迁移到pytest框架的代码不需要重写代码   unittest框架迁移到pytest框架的时候不需要重写代码   纯python代码的自动化测试框架

  pytest对比unitest框架的优势:高级,效率高,支持定制化,支持分布式,支持315种以上的丰富插件,还能向下兼容unitest

  pytest灵活:

    1:定制化(定制化用例执行,定制化报告)

    2:环境清除也灵活   以及各方面做的都比unittest更加灵活

  pytest更加灵活,便捷,效率更高,  还支持分布式(分布式是其他框架做不了)

  分布式:1000个接口用例怎么跑,一条条跑很费劲,时间长,找几个同时分担执行测试用例(pytets独有的性质)

2:pytest框架环境搭建:   pip pytest                安装pytest   pip install pytest-html           安装原生态报告模板--自带的(有点垃圾)   Required-by: pytest-xdist(分布式测试), pytest-metadata, pytest-html, pytest-forked, allure-pytest   100个接口用例,正常是一个个用例跑,时间很长,   分布式-多个业务用例多条线来跑,提高效率(分布式设计用例---分布式逻辑设计,不要出现耦合,关联性太强的东西,否则会等待的)

3:pytets执行测试用例   设计测试用例时候注意点(必须遵循的规则,否则不识别):

    1:.py测试文件必须以test(test_xxx)开头(或者以_test结尾)     2:测试类必须以Test开头,并且不能有init方法-----测试类Test开头     3:测试方法必须以test_开头     4:断言必须使用assert

4:一般做项目是新建package包的    项目文件     lib库文件    (登录接口源代码,其他接口公共的类,封装的库,登录的,订单的)(包)     data文件    (参数化数据,excel文件,yaml文件,csv文件---测试文件,用例,文档)(可以是普通文件夹)     test_case文件  (放测试用例的 )(包)       test_func01.py(测试用例,写的最好见名知意)     report文件    (存放测试报告的普通文件夹)     config      (配置文件)

5:pytest函数级别   函数级别的测试用例必须test_开头:如下test_tc01,test_tc02两个测试用例

 
  1. import pytest

  2. def test_tc01():    #定义函数类型测试用例  

  3. assert 1+1==2   #断言

  4. def test_tc02():

  5. assert 1+1==3   #断言

  6. if __name__ == '__main__':

  7. pytest.main(["test_func01.py"]) #我主动运行我的pytest框架(自动调用所有的test测试函数,按照顺序依次运行,test开头的用例自动识别)

  8. 复制代码

6:pytest类级别(工作一般以类位单元,一个模块一个类,登录类,订单类,购物类)   类级别的测试l类必须以Test开头,并且类李不能有init方法,类里面的函数都是test_开头

  封装好函数和类就行,其他的交给框架,设置好,框架帮你自动组织怎么运行

  封装为了分层,后面更好维护,代码结构整洁

 
  1. import pytest

  2. class Test_login():             #登录模块的测试类

  3.   def test_login01(self):

  4. print("---test_login01----")

  5. assert 1 + 1 == 2

  6.   def test_login02(self):

  7. print("---test_login02----")

  8. assert 1 + 1 == 3

  9. if __name__ == '__main__':

  10.   pytest.main(["test_func01.py","-s"]) #框架自己调用函数  需要打印对应的信息,需要在列表里面加-s

  11. 复制代码

7:自动化测试里面的环境初始化与清除   环境初始化目的:

    清空测试环境的垃圾数据,前置条件  

    需不需要分层:需要

    比如:课程模块:课程模块的初始化需要

      1:删除所有的课程   

      2:新增我们的一些课程(这个给修改/查询/删除接口使用)  模块级别的(大的课程模块第一件事就是删除以前的课程)

        干掉数据后假如需要删除课程,这个接口需要单独的fixture的初始化,增加课程才能删除,其他的接口不需要这个fixture初始化,)

      分层:模块层次的初始化,某个接口也需要初始化----框架的分层

      条件初始化要和接口挂钩,接口该怎么就要怎么设计

  环境初始化和清除,

    一头一尾,两个不同概念,(环境的初始化也可以是清除数据)     一个接口可以多个级别的fixture,可以

    分布式:1:并行执行 2:分布式

    优化运行时间:分布式,(搭建环境麻烦)

  什么是环境初始化:

    做这个用例之前想要做个操作,初始化动作,比如登录,首先需要连上这个项目(要先能ping通),

    环境初始化--比如课程新增需要数据全部清空,也是环境初始化

  功能测试:保证测试环境数据和跑什么系统的,或者后台有什么进程执行,或者项目里面测试这功能,功能里面有没有垃圾数据要清除  做个初始化

  unittest:最基础的框架,python自带(环境初始化和数据清除用setup和teardown)

  jemeter:也有环境清除和初始化

  不管做什么测试比如(功能,自动化,性能)都要对当前测试环境初始化,做完后要垃圾数据进行回收(特别是自动化,不然很多用例明明是对的会失败)

    每次做一个场景,模块的时候,看看模块有没有需要前置的或者环境清除的步骤(基本操作流程)

  pytest是unittest的升级版,对环境清除操作非常灵活(分层分级)

  pytest:fixture操作类进行环境初始化 @fixture这样的一个装饰器

  pytest的fixture操作

    环境初始化与清除

    pytest提供的fixture实现unitest中的setup/teardown功能,可以在每次执行case之前初始化数据

    不同的是,fixture可以只在执行某几个特定case前运行,只需要在运行case前调用即可,比setup/teardown使用灵活

  pytest的初始化和清除可以类里面写个setup_class方法做,以类为单元,模块,包,方法为单元都可以,也可以用fixture来做

8:pytest前置和后置条件(环境初始化与清除)     环境初始化

    1:清除以前的数据

    2:测试的时候不是每个接口都要执行,可以定制化执行,固定执行某些接口,先执行删除用例,

      但是数据已经被清除了,无法删除,修改--需要新增一批测试数据,所以这时候需要环境初始化和清除的想法

  setup_class:类里面类级别的初始化,teardown

  pytest初始化和前置条件,很多接口用例本身需要初始化,初始化分为很多层,

  可以在整个外面做,也可以在里面做,测试类的初始化可以在类里面定义

 
  1. import pytest

  2. class Test_login():           #登录模块的测试类

  3. #该测试类---有个前置的操作(初始化)

  4. def setup_class(self):      #类级别的初始化--可选项

  5. #一个项目,先登录,再购物,登录就是购物类的前置条件,可以放在setup_class里面

  6. print("执行测试类之前,我需要执行操作")

  7. def test_login01(self):

  8. print("---test_login01----")

  9. assert 1 + 1 == 2

  10. def test_login02(self):

  11. print("---test_login02----")

  12. assert 1 + 1 == 3

  13. def teardown(self):            #看业务本身需不需要初始化和清除环境,--可选项

  14. print("------该测试类的环境清除-----")

  15. if __name__ == '__main__':

  16. pytest.main(["test_func01.py","-s"])

  17. 复制代码

9:pyets种有四种级别的setup和teardown   1:setup_module和teardown_module,在整个测试用例所在的文件中所在的文件中所有的方法运行前和运行后运行,只运行一次---模块的   2:setup_class和teardown_class,在整个测试文件中的一个class中所有的用例的签后运行 ----class类   3:setup_method和teardown_method,在class内的每个方法运行前后运行 ---------方法的   4:setup_function和teardown_function,在非class下属的每个测试方法的前后运行 ----函数的     分层分级(不同级别有不同方法)

10:pytest里面的数据初始化装饰器fixture参数说明 fixture_function: Optional[_FixtureFunction] = None, *, scope: "Union[_Scope, Callable[[str, Config], _Scope]]" = "function", --scope参数:级别 params: Optional[Iterable[object]] = None,------------------------------- params:参数 autouse: bool = False,---------------------------------------------------- autouse:是否自动化执行 ids: Optional[ Union[ Iterable[Union[None, str, float, int, bool]], Callable[[Any], Optional[object]], ] ] = None, name: Optional[str] = None  @pytest.fixture(scope=xxx,params=xxx,autouse=xxx) 

    fiixture装饰器可以传单三个参数

      1:scope参数:初始化清除定义级别

       2:params:参数

       3:autouse:是否自动化执行 

  11:fixture 函数级别的初始化,环境初始化

 
  1. import pytest

  2.                 #函数级别的@pytest.fixture()初始化操作

  3. @pytest.fixture()     #标记函数是个初始化操作,标记后需要传给每个函数statr_func这个函数名才会执行初始化操作(函数级别的)

  4. def statr1_func():

  5.            #这不是测试函数,一个普通函数,pytest执行用例只能识别test开头的方法和函数,所以pytest.main不会执行(不参加pytest用例)

  6. print("------初始化操作1------")

  7. @pytest.fixture()

  8. def statr2_func():

  9. print("------初始化操作2------")

  10. #fixture:有哪些操作(可以多个初始化可以一起调,需要两个初始化,需要连接,需要登录)

  11. #这种写法很方便,函数需要statr_func1函数做一个初始化操作可以调用statr_func1这个函数,---def test_001(statr1_func):

  12. # 需要其他初始化方法可以选择性调用其他初始化函数,传递函数名就行(灵活选择)----def test_002(statr2_func):

  13. #函数初始化操作需要传递几个函数也可以多个函数名传递--def test_003(statr2_func,statr1_func):

  14. #方便灵活

  15. def test_001(statr1_func):

  16. print("-----test01------")

  17. def test_002(statr2_func):

  18. print("-----test02 ------")

  19. def test_003(statr2_func,statr1_func):

  20. print("-----test03 ------"

  21. if __name__ == '__main__':

  22. pytest.main(["test_pytest.py","-s"])

  23. 复制代码

** 聪明的小伙伴就发现了,欸出bug了, print("-----test03 ------"这里明显少了个括号嘿嘿嘿,写代码还是要细心一点。记得加上【)】 **

 12:类级别的初始化class,可以使用setup做初始化,也可以使用fixture做初始化

 
  1. import pytest

  2. @pytest.fixture(scope="class")         #类级别的初始化函数 scope="class" 就是把这个初始化定义成类级别的

  3. def statr1_func():

  4. print("------初始化操作1------")

  5. class Test_00:                  #需要执行 Test_00测试类,需要做初始化(可以setup_class)

  6. # def setup_class(self):

  7. # print("类内部的初始化,")      #只对类有用,类级别的,类里只做一次(几个类的初始化操作一样这种不适合,需要重复写)

  8.    #fixture初始化类就是避免重复代码

  9. def test_001(self,statr1_func):

  10. print("-----test01------")

  11. def test_002(self,statr1_func):

  12. print("-----test02 ------")

  13. if __name__ == '__main__':

  14. pytest.main(["test_pytest01.py","-s"])

  15. 复制代码

  类级别初始化fixture,虽然test_001和test_002都调用了statr1_func这个类级别的初始化函数,但是执行类测试用例的时候只执行statr1_func初始函数一次      多个类都可以调用statr1_func这个类级别的初始化方法,调用的时候最好放在类里的第一个函数,后面的函数可以不传(因为对应的是类级别的初始化)   

 
  1. import pytest

  2. @pytest.fixture(scope="class")       #类级别的初始化函数

  3. def statr1_func():

  4. print("------初始化操作1------")

  5. #一个模块里面有函数用例也有类的用例怎么做:(class级别的初始化只对类有用,对函数没用)

  6. def test_003(statr1_func): #测试函数,

  7. print("-----test03------")

  8. class Test_00: #需要执行test00测试类,需要做初始化(可以setup_class)

  9. def test_001(self,statr1_func):

  10. print("-----test01------")

  11. def test_002(self,statr1_func):

  12. print("-----test02 ------")

  13. if __name__ == '__main__':

  14. pytest.main(["test_pytest01.py","-s"])

  15. 复制代码

初始化方法statr1_func定义成class类级别的,函数级别的测试测试用例test__003调用初始化函数会执行一次,   class类级别的测试用例Test_00调用初始化函数会执行一次(一共执行两次)   看级别的,整个模块的级别的化最好用module,否则有问题,fixture可以做return,会有返回值的,对应级别来做, 执行结果:     test_pytest01.py     ------初始化操作1------     -----test03------     .------初始化操作1------     -----test01------     .-----test02 ------

 13:类级别初始化实际代码:初始化操作是登录操作

 
  1. #课程模块的测试类

  2. import pytest

  3. from lib.api_lib.lesson import Lesson

  4. from lib.api_lib.lesson import Login

  5. from tools.execlMethod import get_excelData

  6. import json

  7. import os

  8. @pytest.fixture(scope="class")         #类级别的初始化函数

  9. def start_func():

  10. global sessionid

  11. sessionid = Login().login('{"username":"auto","password":"sdfsdfsdf"}')

  12. class Test_lesson:

  13. #1:课程新增接口,前置条件登录(封装完一个方法后想办法做数据驱动),课程增加需要通过excel表用例来做

  14. @pytest.mark.parametrize("inData,repsData", get_excelData('2-课程模块', 2, 26))

  15. def test_lesson_add(self,start_func,inData,repsData):

  16. reps=Lesson(sessionid).lesson_add(inData)

  17. print(reps)

  18. assert reps["retcode"]==json.loads(repsData)["retcode"]

  19. if __name__ == '__main__':

  20. pytest.main(["test_lesson01.py", "-s", "--alluredir", "../report/tmp"])

  21. os.system("allure serve ../report/tmp")

  22. 复制代码

 14:模块级别的初始化mudule,不管是类还是方法    @pytest.fixture(scope="module")     模块(module)级别的初始化,(整个模块所有的类所有的东西要做一步操作,可以使用module这个模式)

    只在模块运行前面只做一次,后面不做了,哪怕多调用也没用,一个模块里面有test_003函数测试用例,

    也有classTest_00类级别的测试用例,定义一个模块级别的初始化函数statr1_func

    函数里面调用初始化方法def test_003(statr1_func):和类里面的方法调用初始化方法test_001(self,statr1_func):,test_001(self,statr1_func):

    整个模块执行的时候初始化函数都只执行一次(不管你这个模块里面调用多少次)

 
  1. import pytest

  2. @pytest.fixture(scope="module")       #模块级别的初始化函数

  3. def statr1_func():

  4. print("------初始化操作1------")

  5.                           #一个模块里面有函数用例也有类的用例怎么做:(class级别的初始化只对类有用,对函数没用)

  6. def test_003(statr1_func):          #测试函数,

  7. print("-----test03------")

  8. class Test_00:   #需要执行test00测试类,需要做初始化(可以setup_class)

  9. # def setup_class(self):

  10. # print("类内部的初始化,")     #只对类有用,类级别的,类里只做一次(几个类的初始化操作一样这种不适合,需要重复写)

  11. #     #fixture初始化类就是避免重复代码

  12. def test_001(self,statr1_func):

  13. print("-----test01------")

  14. def test_001(self,statr1_func):

  15. print("-----test02 ------")

  16. if __name__ == '__main__':

  17. pytest.main(["test_pytest01.py","-s"])

  18. 复制代码

     执行结果:test_pytest01.py           ------初始化操作1------           -----test03------           .-----test01------           .-----test02 ------

    在这个模块下面所有的都会调用(包级别的,包里面运行前做个环境清除)

    需要在testcase文件夹里面创建一个conftest.py模块,这个

    固定名称,pytest自动识别这个名称

    testcase里面:新增课程前面需要登录,增加课程前面需要清除数据,需要2个级别的初始化,1:登录 2:整个环境的清除

    test_case(测试用例文件夹)创建一个:conftest.py文件  里面写包级别的初始化

    conftest.py文件里也能写类级别和模块级别的初始化,而且不需要调用,这个conftest.py模块是pytest自动识别导入的

 
  1. test_case                #文件夹

  2. conftest.py

  3. import pytest

  4. #包级别的初始化,在运行整个包之前做个初始化,包里面不同作用域,每个包里面都可以放一个,每个包里面的操作都可以不一样

  5. @pytest.fixture(scope="session",autouse=True)         #session级别的处于时候autouse=True默认自动执行

  6. def start_demo(request): #包的开始

  7. print("我是整个包的初始化")

  8. def fin():                            #尾部这是包级别的,整个包做完后做个环境数据的清除 包的结束

  9. print('---测试完成,包的数据清除---')

  10. request.addfinalizer(fin)                   #回调,当我整个包运行完了后回调fin这个方法

  11. #fixture的参数autouse: bool = False,---自动执行参数

  12. 复制代码

    #session的级别,包里面有很多模块,很多模块需要对整个包进行初始化在conftest.py里面做模块的数据初始化和清除(conftest.py只对当前包有用)

  15:两种调用初始化和清除函数的方式+初始化清除函数的返回值的使用

 
  1. import pytest

  2. @pytest.fixture()

  3. def befor_func():

  4. print('xxxxxxxxxxxxx测试用例的初始化xxxxxxxxxxxxxxxx')

  5. yield 10

  6. print('zzzzzzzzzzzzzzzzzz测试用例的清除zzzzzzzzzzzzzz')

  7. def test_001(befor_func):                #调用初始化和清除方式一:直接在测试用例里传递初始化清除函数的函数名来调用

  8. print("测试用例001")

  9. res=befor_func                    #如果初始化清除函数有返回值,可以直接这样接收参数来使用

  10. print(res)

  11. @pytest.mark.usefixtures('befor_func')        #调用初始化和清除方式二:使用usefixtures放在测试用例前面直接调用初始化清除函数

  12. def test_002():

  13. print("测试用例002")

  14. if __name__ == '__main__':

  15. pytest.main(["test1.py",'-s'])

  16. 复制代码

  七:pytest前置条件+后置条件的两种写法

    1:使用yield关键字来是实现  推荐使用这种,因为yield关键字能返回函数的值

 
  1. import pytest

  2. @pytest.fixture()

  3. def befor_func():

  4. print('xxxxxxxxxxxxx测试用例的初始化xxxxxxxxxxxxxxxx')

  5. yield 10                            #yield后面跟的是测试用例的后置条件,支持用例执行后就执行yield里的内容

  6. print('zzzzzzzzzzzzzzzzzz测试用例的清除zzzzzzzzzzzzzz')

  7. def test_001(befor_func):

  8. print("测试用例001")

  9. res=befor_func

  10. print(res)

  11. if __name__ == '__main__':

  12. pytest.main(["test1.py",'-s'])

  13. 复制代码

2:使用finc()函数来实现    这种就不能返回返回值了

 
  1. import pytest

  2. @pytest.fixture()

  3. def befor_func(request):

  4. print('xxxxxxxxxxxxx测试用例的初始化xxxxxxxxxxxxxxxx')

  5. def fin():                        #尾部这是后置条件,测试用例执行后就会调用这个函数      

  6. print('zzzzzzzzzzzz测试用例的清除zzzzzzzzzzz')

  7. request.addfinalizer(fin)               #回调,当我整个包运行完了后回调fin这个方法          

  8. def test_001(befor_func):

  9. print("测试用例001")

  10. if __name__ == '__main__':

  11. pytest.main(["test1.py",'-s'])

  12. 复制代码

16:pytest数据驱动(参数化)    pytest数据驱动的意义:

    参数化(登录用例4条,每一个账号密码都不同,使用框架把4个用例全部执行完,不需要for循环遍历执行,采用数据驱动方案来做)

    pytest内置装饰器@pytest.mark.parametrize可以让测试数据参数化,把测试数据单独管理,类似ddt数据驱动的作用,方便代码和测试数据分离

  @pytest.mark.parametrize("a",[1,2,3]):        参数化传一组参数  

  @pytest.mark.parametrize("a,b", [(1,2),(3,4),(5,6)])    参数化传多组参数

  登录账户密码(name和psw不同的用例组合,一个接口几十个用例怎么做----几十组数据----传的参数不同(什么请求方式和各种都一样)

  可以把name和psw分别采取多组数据进行参数化,数据分离,一个接口跑4次,每次用不同的参数)

 
  1. import pytest

  2. #[(1,2),(3,4),(5,6)] [1,2,3]

  3. class Test_login():

  4. def setup_class(self):

  5. print("执行测试类之前,我需要执行操作")

  6. @pytest.mark.parametrize("a",[1,2,3]) #("变量名",[1,2,3]),数据需要封装成一个列表,多个数据需要封装成列表嵌套元组 ----数据驱动

  7. def test_login01(self,a): #数据驱动,一定要把变量名a引入引来,不然无法参数化

  8. 复制代码

 
  1. print("---test_login01----")

  2. assert 1 + 1 == a

  3. @pytest.mark.parametrize("a,b", [(1,2),(3,4),(5,6)]) #数据驱动传多组参数

  4. def test_login02(self,a,b):

  5. print("---test_login02----")

  6. assert a + 1 == b

  7. def teardown_class(self):

  8. print("------该测试类的环境清除-----")

  9. if __name__ == '__main__':

  10. pytest.main(["test_func01.py","-s"])

  11. 复制代码

17:pytest结合allure报告操作     一:pytest自带的报告框架 pytest-html

  二:allure环境搭建(allure是报告库不是python专属的,很全面的框架)-allure报告漂亮

    1:下载allure.zip(压缩包)     2:解压allure.zip到一个文件目录     3:将allure-2.13.3\bin路径添加到环境变量path     4:pip install allure-pytest -------allure报告本身不是很漂亮,通过allure-pytest这个库可以定制化报告,让报告变得很漂亮     5:验证(cmd输入allure)

  三:allure和pytest联合执行生成报告:运行两条语句

    1:执行pytest单元测试,生成的allure报告需要的数据存在/tmp目录

    pytest -sq --alluredir=../report/tmp   #pytest把allure报告的生成的中间文件放到一个临时文件里面(pytets生成报告,需要数据,所以先把数据存起来)

                      #所有的报告需要数据支持的,数据来源pytest框架本身,结果数据存到一个文件,存在../report/tmp文件夹 

                      #tmp临时文件,一般json格式      

    2:执行命令,生成测试报告

    allure generate ../report/tmp -o ../report/report -clean       #allure指令生成对应报告

  18:allure模拟代码

 
  1. import pytest

  2. import os

  3. class Test_login():

  4. def setup_class(self):

  5. print("执行测试类之前,我需要执行操作")

  6. @pytest.mark.parametrize("a",[1,2,3])

  7. def test_login01(self,a):

  8. print("---test_login01----")

  9. assert 1 + 1 == a

  10. @pytest.mark.parametrize("a,b", [(1,2),(3,4),(5,6)])

  11. def test_login02(self,a,b):

  12. print("---test_login02----")

  13. assert a + 1 == b

  14. def teardown_class(self):

  15. print("------该测试类的环境清除-----")

  16. if __name__ == '__main__':

  17. #需要打印对应的信息,需要在列表里面加-s

  18. #1:--alluredir ---生成临时文件,测试用例的结果数据放到目录 --alluredir 存放目录

  19. pytest.main(["test_func01.py","-s","--alluredir","../report/tmp"]) #框架自己调用函数

  20. #通过--alluredir把allure需要的数据存到../report/tmp这个路径下面

  21. #../--所在路径的父级别目录是test_case的目录隔壁邻居report文件下tmp,专门放alluer报告生成的需要的数据源

  22. # 2:临时数据没有报告的,allure generate allure才会生成报告 -----allure生成器生成allure报告--generate allure生成器,cmd指令

  23. #需要os模块os.system()调用指令可以在local的cmd里面敲

  24. os.system("allure generate ../report/tmp -o ../report/report --clean")

  25. #os.system("allure generate 报告需要的数据 -o 报告存放目录 --clean")

  26. #-o生成

  27. #allure generate生成报告指令,把../report/tmp 的文件-o生成报告out out一下,生成的报告放在../report/report

  28. #--clean把上次报告清除一下用--clean

  29.                  #allure报告生成的是一个服务,(本地服务)和jinkins结合,放在整个里面去集成,放到公共服务器里面

  30. 复制代码

  19:allure报告的优化

 
  1. import pytest

  2. import os

  3. import allure

  4. @allure.feature("登录模块") #一级标题,大模块标题(类标签)

  5. class Test_login():

  6. def setup_class(self):

  7. print("执行测试类之前,我需要执行操作")

  8. @allure.story("登录login01") # 二级标签(每个接口的标签)

  9. @allure.title("login01") # 标题,每个用例带个标题(报告体现在每个测试用例)(一个接口有几个用例,title用例的标签)

  10. @pytest.mark.parametrize("a",[1,2,3])

  11. def test_login01(self,a):

  12. print("---test_login01----")

  13. assert 1 + 1 == a

  14. @allure.story("登录login02") # 二级标签,定制allure报告层级

  15. @allure.title("login02") #标题,每个用例带个标题(报告体现在每个测试用例)

  16. @pytest.mark.parametrize("a,b", [(1,2),(3,4),(5,6)])

  17. def test_login02(self,a,b):

  18. print("---test_login02----")

  19. assert a + 1 == b

  20. def teardown_class(self):

  21. print("------该测试类的环境清除-----")

  22. @allure.feature("购物模块")

  23. class Test_Shopping():

  24. @allure.story("shopping")

  25. @allure.title("shopping01")

  26. @pytest.mark.parametrize("a,b", [(1, 2), (3, 4), (5, 6)])

  27. def test_shopping(self, a, b):

  28. print("---test_login02----")

  29. assert a + 1 == b

  30. if __name__ == '__main__':

  31. pytest.main(["test_func01.py","-s","--alluredir","../report/tmp"])

  32. os.system("allure generate ../report/tmp -o ../report/report --clean")

  33. #allure报告生成的是一个服务,(本地服务)和jinkins结合,放在整个里面去集成,放到公共服务器里面

  34. 复制代码

 20:其他知识点     测试用例一般写在excel表格文件里面,数据分离(维护好excel就行)

    pytest--从头到尾到报告执行发邮件

    字典是一种存储类型,json是一种格式(完全不同)

21:pytest参数解析  

 
  1.  pytest.main(['test_boss.py','-s','-k test_modify_psw','--alluredir=tmp/my_allure_results'])

  2.     test_boss.py         指定测试用例文件,

  3.     -s              显示print语句

  4.     -k test_modify_psw      指定某个测试用例

  5.     -n             表示用两个进程启动测试脚本

  6.     生成报告缓存文件  --alluredir=tmp/my_allure_results

  7.     os.system('allure serve tmp/my_allure_results')    打开测试报告,命令行需要python 的os模块调用

  8. 复制代码

22:pytest的初始化和清除:

 
  1. import pytest

  2. #假设启动被测app的时候需要去填写配置项信息,每个的端口号不同,多终端需要两个appim server

  3. #这时候setup_module和teardown_module不能传参,搞不定,需要换一种方法做测试用例的初始化和清除,

  4.                 #setup_module以模块为作用域,不写module以测试用例(测试函数)为作用域

  5. # def setup_module():    #测试用例之前执行,原始的setup和teardown有个缺陷,里面不能传参数,

  6. #        #默认test级别,每个测试用例执行的时候都会执行一次,希望当前某个模块执行的时候只执行一次(不管里面用例执行多少次)

  7. #        #setup初始化和tear_down升个级,升级成module模块级别的

  8. # print("启动被测app")

  9. # print('连接appium服务')

  10. #

  11. # def teardown_module():

  12. # print('关闭被测app')

  13. # print('断开appium服务')

  14. #定义个函数,名字随便取  使用@pytest.fixture装饰器把这个函数装饰成初始化清除函数

  15. @pytest.fixture(scope='module') #作用域默认test,初始化,加装饰器,初始化清除函数,autouse=True(自动执行)这种方法不建议使用 #

  16. def before_test(): #初始化函数升级作用域到module模块级别

  17. print("启动被测app")

  18. print('连接appium服务')

  19. yield #后面写清除动作,

  20. after_test()

  21. #清除函数,清除函数并不会直接被初始化函数使用,我们必须放在初始化函数yiled后面才能回被调用

  22. def after_test():

  23. print('关闭被测app')

  24. print('断开appium服务')

  25. #目前一共有两个port,需要测试两个手机,两个多终端,before_test需要装饰器标记

  26. #测试用例的参数化

  27. @pytest.mark.usefixtures('before_test') #这表示调用某个自定义的初始化函数,括号里面的字符串写被调用函数的名字

  28. @pytest.mark.parametrize('psw',['boss123','boss456'])

  29. def test_app(psw): #测试用例,可能涉及到其他参数,比如需要一些配置信息,测试用例涉及到参数,

  30.                               #多组参数需要使用装饰器pytest.mark.parametrize(数据驱动),psw传参和形参名字对应的

  31. print('测试boss app')

  32. print(f'登录测试账号{psw}')

  33. if __name__ == '__main__':

  34. pytest.main(['pytest_ywt.py','-s'])

  35. 复制代码

23:pytest之:不只是测试函数test_app能参数化,初始化函数before_test也能参数化   重点:测试用例的参数化+初始化清除函数的参数化  初始化清除函数的参数化能够实现appium的多终端测试

    初始化清除函数的参数化,方法很多种:

    before_test初始化函数注入参数,因为print(f'连接appium服务{port}')里面port需要变化的,

    @pytest.fixture(scope='module',params=[(4723,),(4727,)])  :初始化清除函数的参数化

      始化函数装饰器里面加params参数传参,port=request.param[0] 来调用params里的参数

      #初始化清除函数的参数化:只传单个参数 import pytest @pytest.fixture(scope='module',params=[(4723,),(4727,)]) #初始化清除函数的参数化params def before_test(request):
port=request.param[0]               #param[0],假如注入多个参数一个port和一个data--需要params传元组, #params=[(4723,100),(4727,200)],一个参数的话不需要写成列表嵌套元素,                                       #params[0]代表获取元组第一个 print("启动被测app") print(f'连接appium服务{port}') yield #后面写清除动作, after_test()                               #request是pytest的对象,我们在用对象里面的方法的时候pycham不会自动帮我们取显示名字,                                       #它也不知道request里面到底什么内容

 
  1. def after_test():

  2. print('关闭被测app')

  3. print('断开appium服务')

  4. @pytest.mark.usefixtures('before_test')

  5. @pytest.mark.parametrize('psw',['boss123','boss456'])

  6. def test_app(psw):

  7. print('测试boss app')

  8. print(f'登录测试账号{psw}')

  9. if __name__ == '__main__':

  10. pytest.main(['pytest_ywt.py','-s'])   

  11.       #初始化清除函数的参数化:传多个参数              

  12.        import pytest

  13.        @pytest.fixture(scope='module',params=[(4723,'xiaomi'),(4727,'meizu')])

  14. def before_testquest):

  15. port=request.param[0]       #param[0],假如注入多个参数一个port和一个data,需要params传元组,params=[(4723,100),(4727,200)],

  16.     #一个参数的话不需要写成列表嵌套元素,request.params[0]代表获取元组第一个

  17. device=request.param[1]     #request.param[1]对应元素里面第二个参数,

  18. print(f"在{device}启动被测app")

  19. print(f'连接appium服务{port}')

  20. yield #后面写清除动作,

  21. after_test()

  22.                         #request是pytest的对象,(固定写法:request.param)

  23.                                 #我们在用对象里面的方法的时候pycham不会自动帮我们去显示名字,它也不知道request里面到底什么内容

  24. def after_test():

  25. print('关闭被测app')

  26. print('断开appium服务')

  27. @pytest.mark.usefixtures('before_test')

  28. @pytest.mark.parametrize('psw',['boss123','boss456'])

  29. def test_app(psw):

  30. print('测试boss app')

  31. print(f'登录测试账号{psw}')

  32. if __name__ == '__main__':

  33. pytest.main(['pytest_ywt.py','-s'])

  34. 复制代码

24:pytest框架执行代码也能在cmd里面直接输入命令执行     xxx\test_case> pytest -s   在test_case这个目录执行会运行test_case文件里面所有的测试文件(test开头的测试用例)

25:分布式   设置用例每个模块独立,有什么前置做到模块里面,比如测试10个模块,用相关联来做,不能做分布式(并发执行)

  每个模块独立还能定制执行那个模块,关联性太强做不到  

  最好做到每个接口都独立化(前置条件做好)不要做太大关联性的接口

  每一层都能做环境清除和定制化(包,模块。类,函数)分层,为后面mark(定点执行哪些用例)和分布式打基础

  分布式:必须做到用例的隔离(低耦合,高内聚),用例走串行风险很大,很难维护

    3000个请求,全部独立化,然后分布式来做(效率提高几倍--几十倍)

26:分布式的实现   分布式的核心点:封装设计:相互独立,登录和课程相互独立,至少模块为单元要相互独立,封装相互独立,接口用例之间最好也相互独立  才能进行分布式

  一:pytest分布式环境搭建和理论介绍:

    第一步:安装一个库 pip install pytest-xdist   分布式运行插件,可以做分布式(这个库有两种运行方式)

    运行方式:

      1:串行,顺序运行,串行运行,从头到尾

      2:并行:pytest-xdist做分布式有两种,一种多核,一种多台机器

        一:多核:单机多核来做(同时跑) 使用-n参数

          电脑多核有假有真:超线程技术(8内核搞成16核),真8核假8核---

          cpu个数:硬件,几个cpu槽,i9900.i710--一般电脑就算一个cpu,单cpu,服务器可能有多个cpu

          核数: 电脑的核数,

          逻辑核数:逻辑核数可以虚拟化,8核可以变成16核(超线程技术)

          多核的话xdist本身的多核的话一般用逻辑核数来做的

        二:多机(可以使用虚拟机)---需要搭环境,多台机器 很麻烦,装环境,下库

  二:测试用例比较多怎么办:分布式 两种情况  

    1:量大:多机 (需要文件报告收集还需要搭环境,做起来比较麻烦)

    2:单机多核  很简单,加-n 参数就行 (做ui和需要一些时间等待的时候时间优化特别明显)

    串行运行:本身是线程去跑的,python就一个进程,里面很多线程,

    走进程的话需要多台机器来做,分量, 用例设计不好会有大问题,数据不对(用例一定要独立化)

    并行和多机:用例一定要设计好,不然数据容易出错,逻辑独立(不能有任何关联,不能有前后关系)  数据和代码封装时候独立化

  三:分布式运行代码

 
  1. #验证单机多核分布式

  2. 复制代码

 
  1. import pytest

  2. import time

  3. def test_01():

  4. time.sleep(3)

  5. print("-----test01-----")

  6. def test_02():

  7. time.sleep(3)

  8. print("-----test01-----")

  9. if __name__ == '__main__':

  10. # pytest.main(["test_xdist.py","-sq"])         #这是串行跑的 6s

  11. pytest.main(["test_xdist.py", "-sq","-n","8"])     #单机多核并行 加"-n","8" 参数 ,用8个核来跑,5.41s,时间少了

  12. #或者测试用例文件目录下 cmd,输入 pytest test_xdist.py -n 8 也可以 这样cmd里执行看到的结果更直观,

  13. #多核来跑在ui里面时间提升很大,ui里面很多地方需要sleep,等待元素(有等待的的提升比较大)多核跑更快

  14. #有等待的情况用多核跑效果越明显

  15. 复制代码

 
  1. 串行运行本身按照线程去跑的,python本身就一个进程,里面很多线程

  2. 走进程的话,多台机器做比较合适---分量,

  3. 用例一定要设计好,不然数据容易出错,逻辑独立(不能有任何关联,不能有前后关系)----数据和代码封装时候独立化

  4. 复制代码

pytest cmd执行多个模块用例:pytest test_xdist.py test_login.py -sq   :运行两个.py文件(写多个运行多个) pytest cmd执行多个包的用例:pytest test_xdist test_login -sq   :运行test_xdist包和test_login 包 还可以运行不同模块的两个包,加包路径 case/test_xdist.py case2/test2_xdist.py 27:pytest的用例定制化执行  mark标签,   所有的接口不需要全部都跑(冒烟,定制化执行某些指定的业务,)     "-m","test_lesson_add" 

  一:pytest框架mark标签  标记非常丰富  mark标签

   mark标签:对于pytest,我们可以再每一个模块,每一个类,每一个方法和用例前面都加上mark,

        那样我们在pytest运行的时候就可以只运行带有该mark标签的模块,类,用例  

        这样的话可以方便我们选择执行自动化时,是执行全部用例,某个模块用例,

        某个流程用例,某个单独用例,总之就是可以某个单独的标签下所有用例

  mark可以标记不同层次的东西(类,函数,方法都可以标记)文件不用标记(本身就可以定制化执行)

  @pytest.mark.lessson_moudle   给测试类贴个标签,标签名字叫lessson_moudle标识课程模块,

    各个函数,类都可以贴上标签(类似别称),选择某个标签就运行某一个(灵活方便)

  什么都不选中照常运行,(全部运行,没有限制)

  mark标签pytest运行可能报错,

    PytestUnknownMarkWarning报错:是一个标签的mark警告,整个pytest这么写不识别你,但是不会报错,只是警告,

    消除警告(增加标签栏,相当于标签的声明)

    标签声明写法:teach_sq文件夹里创建一个pytest.ini的文件(pycham需要安装ini插件 file-setting-plugins(搜索ini)社区版似乎不行)

      pycham找不到可以离线装

 
  1. teach_sq

  2. pytest.ini-----文件内容如下,相当于pytest的mark标签声明一下

  3. 复制代码

            #文件内容,markers后面把标签全部写上,    lessson_moudle 一个类级别的mark标签名,             #test_lesson_add,test_lesson_list,test_lesson_delete 三个函数级别的mark标签名称             #  mark标签名称: 描述 这样的格式来写,前面声明标签名称,后面是描述(随便写)

 
  1. [pytest]                    

  2. markers=

  3. lessson_moudle: teach_lesson        (标签名: 描述 这样的格式   前面lesson_modle和test_lesson_add等是mark的标签名称,

  4. 复制代码

                                        声明一个标签    :冒号后面一定要加空格,规范)

 
  1.     后面的描述不建议写中文,会报错(需要转码,但是这个文件python自己调用的)

  2. test_lesson_add: teach_lesson

  3. test_lesson_list: teach_lesson

  4. test_lesson_delete: teach_lesson

  5. 复制代码

  定制化执行test_lesson_add一个接口: "-m","test_lesson_add"

  pytest.main(["test_lesson01.py","-s","-m","test_lesson_add"]) ---- -m就可以实现,mark定制化执行

  定制化执行多个接口--逻辑或就行:"-m","test_lesson_add or test_lesson_delete"

  pytest.main(["test_lesson01.py","-s","-m","test_lesson_add or test_lesson_delete"])

  排除法排除一个-定制化除了某个接口不运行其他都运行---"-m","not test_lesson_add"

  pytest.main(["test_lesson01.py","-s","-m","not test_lesson_add"])----除了test_lesson_add这个接口其他都运行

  排除法排除多个-"-m","not (test_lesson_add or test_lesson_delete)"

  pytest.main(["test_lesson01.py","-s","-m","not (test_lesson_add or test_lesson_delete)"])

  筛选测试用例代码:

 
  1. import pytest

  2. @pytest.mark.zzzzz

  3. def test_001():

  4. print('test_001')

  5. def test_002():

  6. print('test_001')

  7. if __name__ == '__main__':

  8. pytest.main(["test1.py",'-s','-m','zzzzz'])

  9. 复制代码

第五章 接口自动化测试必备技能-HTTP协议

1. HTTP协议简介及状态码解析
  1. HTTP协议简介
  • HTTP协议,中文名称为超文本传输协议
  • 它是一个应用层的协议,私有请求和响应构成
  • 其典型的引用场景就是浏览器和服务器之间进行数据传输
  1. HTTP协议的基本工作流程

3. HTTP协议状态码解析 5种状态分类

  • 1**:服务器收到请求,需要请求者继续执行操作
  • 2**:操作被成功接受并处理
  • 3**:请求长定向,需要进一步的操作以完成请求
  • 4**:客户端错误,请求包含语法错误或无法完成请求
  • 5**:服务器错误,服务器在处理请求的过程中发生了错误
  1. HTTP协议常见具体的状态码 200:请求成功 400:客户端请求的语法错误,服务器无法理解 401:请求要求用户的身份认证 403:服务器理解请求客户端的请求,但是拒绝执行此请求 404:服务器无法根据客户端的请求找到资源 405:客户端请求中的方法被禁止 500:服务器内部错误,无法完成请求 503:由于超载或系统维护,服务器暂时的无法处理客户端的请求 505:服务器不支持请求的HTTP协议的版本,无法完成处理
2. HTTP协议请求头解释
  • Axxept:告诉服务器自己接受什么数据类型
  • Axxept-Encoding:浏览器申明自己接收的编码方法,通常指定压缩方法,是否支持压缩,支持什么压缩方法
  • Accept-Language:浏览器声明自己接收的语言
  • Connection:表示是否需要持久连接
  • Cookie:保持客户端部分信息发送到服务端验证和使用
  • Host:客户端指定自己想访问的WEB服务器的域名/IP地址和端口号
  • jwt-token:代表了客户端向服务端请求时带有什么验证信息
  • Origin:代表了客户端的请求来自于哪个站点
  • Referer:浏览器向WEB服务器表明自己是从哪个网页URL获取得点击当前请求中的网址/URL
  • User-Agent:浏览器表明自己的身份(是那种浏览器)
  • X-Requested-With:标识着请求是同步请求还是异步请求
  • Cache-Control:指定请求和响应遵循的缓存机制,决定了如何管理和使用缓存

3. HTTP协议响应头解析
  • 以下图圈住就是响应头

  • Access-Control-Allow-Credentials:请求的响应是否可以暴露于该页面,当true值返回时它可以被暴露
  • Access-Control-Allow-Origin:服务器端允许进行跨域资源访问的来源域
  • Content-Type:服务端告诉客户端响应体的内容类型
  • Date:创建响应数据(报文)的日期时间,就是消息发送的时间
  • Transfer-Encoding:传输编码,chunked是分开传输的意思
  • Vary:服务器用这个头的内容告诉缓存服务器,在什么条件下才能用本响应所返回的对象响应后续的请求
  • Allow:服务器支持哪些请求方法(如GET,POST等)
  • Server:服务器表明自己是什么软件及版本等信息
4. Cookie和Session的工作机制及区别
  1. 什么是Cookie?
  • Cookie是一直网站为了识别用和跟踪会话而存储在用户本地终端中的文本数据
  1. Cookie的工作流程

  1. 进入开发者工具路径

4. 什么是Session?

  • Session是指会话控制,是保存在服务器上一种机制,当客户端访问服务器的时候,服务器会把信息以某种形式记录在服务器上,恰恰和Cookie相反。
  • 以下圈住的就是id

  1. Session的工作流程

  2. Cookie和Session的区别
  • 存放位置不同,Cookie放在客户端,Session存在服务端
  • 存放位置不同导致安全性不同,Cookie存在客户端中,用户可以任意分析Cookie,而Session在服务端,用户分析不到
  • 数据存储大小不同,Cookie保存的数据不能超过4K,Session存在服务端,所以没有限制
  • 性能使用情况不同,当访问过多时Session比较占用服务器资源,而Cookie不会

六. requests接口请求

1. requests库介绍与安装
  1. requests库介绍
  • requests是一款非常火爆且常用的Python三方库
  • 能够实现HTTP协议的各种请求方法
  • 使用简单易上手
  1. requests库的安装方法
  • pip install requests
  • 安装成功

2. requests发送get接口请求
  • 查看百度的响应码:
 
  1. import requests

  2. response = requests.get("http://www.baidu.com")

  3. print(response)

  4. print(response.status_code)

  5. print(response.text)

  6. 复制代码

  • 打印结果:
 
  1. <Response [200]>

  2. 200

  3. <!DOCTYPE html>

  4. <!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta content=always name=referrer><link rel=stylesheet type=text/css href=http://s1.bdstatic.com/r/www/cache/bdorz/baidu.min.css><title>ç¾åº¦ä¸ä¸ï¼ä½ å°±ç¥é</title></head> <body link=#0000cc> <div id=wrapper> <div id=head> <div class=head_wrapper> <div class=s_form> <div class=s_form_wrapper> <div id=lg> <img hidefocus=true src=//www.baidu.com/img/bd_logo1.png width=270 height=129> </div> <form id=form name=f action=//www.baidu.com/s class=fm> <input type=hidden name=bdorz_come value=1> <input type=hidden name=ie value=utf-8> <input type=hidden name=f value=8> <input type=hidden name=rsv_bp value=1> <input type=hidden name=rsv_idx value=1> <input type=hidden name=tn value=baidu><span class="bg s_ipt_wr"><input id=kw name=wd class=s_ipt value maxlength=255 autocomplete=off autofocus></span><span class="bg s_btn_wr"><input type=submit id=su value=ç¾åº¦ä¸ä¸ class="bg s_btn"></span> </form> </div> </div> <div id=u1> <a href=http://news.baidu.com name=tj_trnews class=mnav>æ°é»</a> <a href=http://www.hao123.com name=tj_trhao123 class=mnav>hao123</a> <a href=http://map.baidu.com name=tj_trmap class=mnav>å°å¾</a> <a href=http://v.baidu.com name=tj_trvideo class=mnav>è§é¢</a> <a href=http://tieba.baidu.com name=tj_trtieba class=mnav>è´´å§</a> <noscript> <a href=http://www.baidu.com/bdorz/login.gif?login&amp;tpl=mn&amp;u=http%3A%2F%2Fwww.baidu.com%2f%3fbdorz_come%3d1 name=tj_login class=lb>ç»å½</a> </noscript> <script>document.write('<a href="http://www.baidu.com/bdorz/login.gif?login&tpl=mn&u='+ encodeURIComponent(window.location.href+ (window.location.search === "" ? "?" : "&")+ "bdorz_come=1")+ '" name="tj_login" class="lb">ç»å½</a>');</script> <a href=//www.baidu.com/more/ name=tj_briicon class=bri style="display: block;">æ´å¤äº§å</a> </div> </div> </div> <div id=ftCon> <div id=ftConw> <p id=lh> <a href=http://home.baidu.com>å³äºç¾åº¦</a> <a href=http://ir.baidu.com>About Baidu</a> </p> <p id=cp>&copy;2017&nbsp;Baidu&nbsp;<a href=http://www.baidu.com/duty/>使ç¨ç¾åº¦åå¿è¯»</a>&nbsp; <a href=http://jianyi.baidu.com/ class=cp-feedback>æè§åé¦</a>&nbsp;京ICPè¯030173å·&nbsp; <img src=//www.baidu.com/img/gs.gif> </p> </div> </div> </div> </body> </html>

  5. 复制代码

 
  1. import requests

  2. # response = requests.get("http://www.baidu.com")

  3. # print(response)

  4. # print(response.status_code)

  5. # print(response.text)

  6. url = "http://localhost:5000"

  7. # 无参数的get请求

  8. r1 = requests.get("http://localhost:5000")

  9. print(r1.text)

  10. # 带路径的get请求

  11. r2 = requests .get(url + "hello")

  12. print(r2.text)

  13. # 路径上带参数的get请求

  14. r3 = requests.get(url + "hello/我是参数")

  15. print(r3.text)

  16. # 路径上带参数,并且带有参数值得get请求

  17. r4 = requests.get(url + "hello/args/我是参数11?key=我是key&value我是value")

  18. print(r4.text)

  19. # 第二种带参数的方式

  20. r5 = requests .get(url + "hello/args/我是参数222",{"key":"=我是key2","value":"我是value2"})

  21. print(r5.text)

  22. 复制代码

 
  1. hello world qqqqqqqq

  2. hello22222

  3. hello22222我是参数

  4. hello22222我是参数::::我是key::::我是value

  5. hello22222我是参数::::我是key2::::我是value2

  6. 复制代码

3. requests发送post接口请求
  • 发送post请求:
 
  1. import requests

  2. url = "http://localhost:5000"

  3. r1 = requests.post(url + "mypost")

  4. print(r1.text)

  5. # 请求数据是表单类型的数据

  6. r2 = requests.post(url + "myposy1",

  7. data={

  8. "username":,"我是名字",

  9. "sex":"我是性别"

  10. })

  11. print(r2.text)

  12. # 请求时json类型的数据

  13. r3 = requests.post(url + "mypost2",

  14. json={

  15. "user":"我是json的user的key",

  16. "value":"我是value"

  17. })

  18. print(r3.text)

  19. 复制代码

  • 打印结果:
 
  1. post request

  2. post request我是名字::我是性别

  3. post request{'user':'我是json的user的key','value':'我是value','sex':'男'}

  4. 复制代码

4. requests发送请求头信息

http://111.231.103.117:8083/swagger-ui.html#/

一、Requests发送HTTP请求 案例:选定某个测试的URL,利用requests库内置的不同的方法来发送不同类型(GET/POST/PUT/DELETE)的http请求,并输出响应的状态码

 
  1. # 导入requests库

  2. import requests

  3. # 定义base_url作为基础被测URL

  4. base_url = 'http://httpbin.org'

  5. # 发送get请求;打印响应状态码

  6. r = requests.get(base_url+'/get')

  7. print(r.status_code)

  8. # 发送POST请求;打印响应状态码

  9. r = requests.post(base_url+'/post')

  10. print(r.status_code)

  11. # 发送PUT请求;打印响应状态码

  12. r = requests.put(base_url+'/put')

  13. print(r.status_code)

  14. # 发送DELETE请求,打印响应状态码

  15. r = requests.delete(base_url+'/delete')

  16. print(r.status_code)

  17. 复制代码

执行结果: 发送4种不同请求,对应响应状态码都是200,请求发送OK

二、Requests参数传递 1.传递URL参数 案例:利用Requests库,在GET请求中使用查询字符串(Query String)传递参数。

 
  1. # 导入requests库

  2. import requests

  3. # 定义base_url作为基础被测URL

  4. base_url = 'http://httpbin.org'

  5. # 定义请求所需的参数,参数之间以英文逗号隔开

  6. param_data = {'uname':'Test00001','pwd':'123456'}

  7. # 发送GET请求,格式如:requests.get(url,params)

  8. r = requests.get(base_url+'/get',params=param_data)

  9. print(r.url) # 输出请求的url

  10. print(r.status_code) #输出响应的状态码

  11. 复制代码

可见查询字符串参数可以使用param进行接收,参数定义为字典格式即可;

  • 2.传递Body参数 案例:利用Requests库,在POST请求的请求体(Request Body)中传递参数
 
  1. import requests

  2. base_url = 'http://httpbin.org'

  3. form_data = {'uname':'Test00002','pwd':'123456'}

  4. # 发送POST请求,格式如:requests.post(url,data)

  5. r = requests.post(base_url+'/post',data=form_data)

  6. print(r.text) # 返回响应内容

  7. 复制代码

body参数在request中使用data接收的情况比较多;

三、Requests请求头设置 1.设置Request Headers 案例:利用Requests库,为POST请求添加HTTP Headers,此时需要传递一个字典类型的数据给headers参数

 
  1. import requests

  2. base_url = 'http://httpbin.org'

  3. form_data = {'uname':'Test00003','pwd':'123456'}

  4. header = {'user-agent':'Mozilla/5.0'}

  5. r = requests.post(base_url+'/post',data=form_data,headers=header)

  6. print(r.text) #以文本形式返回响应内容

  7. 复制代码

print(r.text)代表以文本形式返回响应内容

若以上代码修改为print(r.json()),代表以json形式返回响应内容;

2.Request Headers爬虫应用 爬虫程序通过定制Request Headers来模拟浏览器的访问行为,以应对网站的反爬虫策略,避免被封。 1 如:爬取知乎页面元素时,需要设置与浏览器一致的请求头,然后再发送请求 测试url:www.zhihu.com/explore (从浏览器Copy一份User-Agent作为设置的Headers)

 
  1. import requests

  2. header = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'}

  3. r = requests.get('https://www.zhihu.com/explore',headers=header)

  4. print(r.text)

  5. 复制代码

总结

在本文中,我们以提高脚本开发效率为前提,一步一步打造了一个简易的测试框架,但因水平所限,并未涉及测试数据初始化清理、测试中如何MOCK等话题,前路依然任重而道远,希望给大家一个启发,不足之处还望多多指点,非常感谢。

最后我邀请你进入我们的【软件测试学习交流群:785128166】, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路

作为一个软件测试的过来人,我想尽自己最大的努力,帮助每一个伙伴都能顺利找到工作。所以我整理了下面这份资源,现在免费分享给大家,有需要的小伙伴可以关注【公众号:程序员二黑】自提!

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

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

相关文章

网工内推 | 外企网工,思科认证优先,弹性工作,补贴多

01 淳华科技&#xff08;昆山&#xff09;有限公司 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1.全厂网络规划 2.Cisco交换机和路由器的配置 3.日常设备点检\维护\配置 4.网络设备的评估并做报告说明 任职要求&#xff1a; 1.具有一定的网络工作经验有Cisco或是其…

DNS域名系统 | unbound

目录 DNS 命名空间和域名结构 DNS的命名空间的结构: 域名服务器的分类&#xff1a; ​编辑 DNS 资源记录 常见type: DNS报文结构 请求报文&#xff1a; 响应报文&#xff1a; 解析类型 递归查询 迭代查询 DNS劫持 DNS劫持方法&#xff1a; 防御措施 DNS服务部署…

05_Scala运算符

文章目录 **1.Scala运算符****2.scala中没有 --等语法****3.逻辑运算符和Java完全相同****4.scala认为万物皆对象** 1.Scala运算符 Scala底层 使用的是equals() 程序员比较两个量的时候&#xff0c;谁来没事比较内存地址&#xff1f; Java中引用数据类型比较地址&#xff0…

Allure精通指南(05)定制化报告内容(环境信息、图标、缺陷类别)

文章目录 Allure 自定义测试环境信息Allure 自定义缺陷类别信息Allure 自定义图标步骤一步骤二步骤三 Allure 自定义测试环境信息 步骤 1&#xff1a;创建 environment.properties 文件 在项目根目录或任何其他不会被--clean-alluredir参数影响的目录下创建 environment.proper…

OpenHarmony语言基础类库【@ohos.util.LightWeightMap (非线性容器LightWeightMap)】

LightWeightMap可用于存储具有关联关系的key-value键值对集合&#xff0c;存储元素中key值唯一&#xff0c;每个key对应一个value。 LightWeightMap依据泛型定义&#xff0c;采用轻量级结构&#xff0c;初始默认容量大小为8&#xff0c;每次扩容大小为原始容量的两倍。 集合中…

1、opencv介绍与开发环境搭建

1、opencv介绍 OpenCV 是 Intel 开源计算机视觉库&#xff0c;是一个跨平台的开源计算机视觉和机器学习软件库。它由一系列 C 函数和少量 C 类构成&#xff0c;可用于开发实时的图像处理、计算机视觉以及模式识别程序。 该库有 2500 多种优化算法&#xff0c;其中包括一套全面…

python怎么输出倒序

python怎么输出倒序&#xff1f;下面给大家介绍四种方法&#xff1a; 创建测试列表 >>> lst [1,2,3,4,5,6]方法1&#xff1a; >>> lst.reverse() #reverse()反转 >>> lst [6, 5, 4, 3, 2, 1] 方法2&#xff1a; >>> lst1 [i for i in …

2024年最好用的10款ER图神器!

分享10款ER图工具&#xff0c;详细分析他们的功能特点、价格和适用场景&#xff0c;可以根据你的需求进行选择。ER图&#xff08;Entity-Relationship Diagram&#xff09;是数据库设计中常用的一种模型&#xff0c;用于描述实体之间的关系。这种图形化的表示方法旨在帮助人们理…

数据结构——二叉树练习(深搜广搜)

数据结构——二叉树练习 路径之和深度优先算法和广度优先算法二叉搜索树判断一棵二叉树是否为搜索二叉树和完全二叉树 我们今天来看二叉树的习题&#xff1a; 路径之和 https://leetcode.cn/problems/path-sum-ii/ 这是一个典型的回溯&#xff0c;深度优先算法的题&#xff0c…

解决Win10 C盘扩展卷灰色不可用的简单方法!

当你发现电脑C盘空间不足&#xff0c;却又一段Win10 C盘扩展卷选项无法使用的状况时&#xff0c;该如何应对呢&#xff1f;本篇文章将向你介绍3种简单的方法&#xff0c;帮助你轻松解决C盘扩容的问题&#xff01; C盘扩容的重要性&#xff1f; 当前&#xff0c;大部分台式机和…

普乐蛙VR航天航空体验馆VR双人旋转座椅元宇宙VR飞船

多长假来袭&#xff01;&#xff01;想为门店寻找更多新鲜有趣的吸粉体验&#xff1f;想丰富景区体验&#xff1f;别着急&#xff0c;小编为你准备了一款爆款设备——时光穿梭机&#xff0c;720无死角旋转&#xff01;&#xff01;吸睛、刺激体验&#xff0c;将亲子、闺蜜、情侣…

【链表】Leetcode K个一组翻转链表

题目讲解 25. K 个一组翻转链表 算法讲解 虽然这道题是一道困难题&#xff0c;但是从代码层面很简单&#xff0c;只是一道简单的模拟&#xff1a;我们要先求出总共需要翻转的链表有多少组&#xff08;链表的长度 / k&#xff09;&#xff0c;接下来就是翻转k的链表最链接的问…

【论文速读】|理解基于大语言模型的模糊测试驱动程序生成

本次分享论文&#xff1a;Understanding Large Language Model Based Fuzz Driver Generation 基本信息 原文作者&#xff1a;Cen Zhang, Mingqiang Bai, Yaowen Zheng, Yeting Li, Xiaofei Xie, Yuekang Li, Wei Ma, Limin Sun, Yang Liu 作者单位&#xff1a;南洋理工大学…

Zephyr sensor子系统学习

一、背景 2023年7月份nRF Connect SDK 2.4.0最新版本&#xff0c;使用的Zephyr V3.3版本。从Zephyr 3.5版本在子系统中加入了sensing子系统。 现在最新的nRF Connect SDK 2.6.0 release支持v3.5.99-ncs1&#xff0c;已经支持sensing子系统 nRF52840现在官方支持两个传感器de…

2023最新!nginx安装配置保姆级教程

2023最新!nginx安装配置保姆级教程 这篇文章了参考了这位的教程:https://blog.csdn.net/qq_36838700/article/details/129971765 导航 文章目录 2023最新!nginx安装配置保姆级教程一、nginx下载二、编译安装nginx安装pcre安装openssl、zlib、gcc依赖安装nginx 二、拓展 一、n…

01.Scala概述及环境配置

文章目录 [toc] 1.**Scala概述**2.**Scala环境搭建**2.1下载2.2环境变量配置 1.Scala概述 特点&#xff1a; 同样运行在JVM上&#xff0c;可以与现存程序同时运行。可直接使用Java类库。同Java一样静态类型。语法和Java类似&#xff0c;比Java更加简洁&#xff08;简洁而并不…

Python-100-Days: Day01

Day01 Python简介 1.1989年Guido von Rossum在圣诞节之夜开始着手python语言编译器的编写。 2.1991年2月 Python v1 编译器诞生&#xff0c;使用C实现的&#xff0c;此时可以调用C的库函数。 3.1994年1月&#xff0c;Python v1.0 正式版发布。 4.2000年10月16日&#xff0…

2024软件测试面试题总结

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 测试技术面试题 1、什么是兼容性测试&#xff1f;兼容性测试侧…

农业-大量数据在数据库中做AVG如何优化

如果是直接查询呢&#xff1f; 设备每个分区3s回传一次数据 一个设备有三个分区 一分钟需要回传 3 * 20 60次 一个小时 回传 60*60 3600次 一天回传 3600 * 24 86400次 我如果想计算以天为单位的气温数据&#xff0c;需要聚合8w条数据 进行优化 一分钟60次&#xff0c…

To String的几个作用

To String的几个作用 一、Object类中toString的作用 1、在主方法中我们可以直接用toString输出对象其中的内容 2、我们需要直接输出对象中所属内容时&#xff0c;直接使用toString方法输出语句&#xff0c;输出内容不友好&#xff0c;不便于阅读 子类&#xff1a; public c…
最新文章