本文大纲截图:
-
UnitTest框架:
- PyTest框架:
框架: 框架英文单词 framework,为解决一类事情的功能的集合。需要按照框架的规定(套路)去书写代码。
一、UnitTest框架介绍【文末分享自动化测试学习资源】
1、什么是UnitTest框架?
- 概念:UnitTest是python自带的一个单元测试框架,用它来做单元测试
- 自带的框架:不需要单独按照,只要安装了 python就可以用
- 第三方框架:想要使用 需要先安装后使用(如:pytest)
- 单元测试框架:主要用来做单元测试,一般单元测试是开发做的。对于测试来说,UnitTest 框架的作用是自动化脚本(用例代码)执行框架(使用UnitTest框架来管理 运行多个测试用例的)
2、为什么使用UnitTest框架?
-
1)能够组织多个用例去执行
-
2)提供丰富的断言方法(让程序代码代替人工自动的判断预期结果和实际结果是否相符)
-
3)能够生成测试报告
-
4)UnitTest框架对于测试来说的作用是:自动化脚本执行框架(使用 UnitTest框架来管理运行多个测试用例的脚本)
3、UnitTest框架核心组成要素
1)TestCase【最核心的模块】
- TestCase(测试用例),这个测试用例是UnitTest框架的组成部分,不是手工和自动化中所说的用例(Test Case)
- 主要作用:每个TestCase(测试用例)都是一个代码文件,在这个代码文件中 来书写 真正的用例代码
2)TestSuite
-
TestSuite(测试套件),用来 管理、组装(打包)多个 TestCase(测试用例)的
3)TestRunner
-
TestRunner(测试执行,测试运行),用来 执行 TestSuite(测试套件)的
4)TestLoader
-
TestLoader(测试加载),功能是对 TestSuite(测试套件)功能的补充,管理、组装(打包)多个 TestCase(测试用例)的
5)Fixture
- Fixture(测试夹具),书写在TestCase(测试用例)代码中,是一个代码结构,可以在每个方法执行前后都会执行的内容
- 每个用例中重复的代码就可以写在 Fixture 代码结构中,只写一遍,但每次用例方法的执行,都会执行Fixture中的代码
- 举例:登录的测试用例
1、打开浏览器
2、输入网址
4、UnitTest框架实践
4.1 TestCase(测试用例)
介绍:
-
1)每个 TestCase(测试用例)都是一个代码文件,在这个代码文件中 来书写真正的用例代码
-
2)代码文件的名称必须按照标识符的规则来书写(可以将代码的作用在文件的开头又多行注释说明)
注意:
-
1)代码文件的命名规范
-
1>代码文件名字不能以数字开头
-
2>代码文件名字中不能有空格
-
3>代码文件名字不能有中文
-
4>代码文件名字由字母、数字、下划线组成,且不能以数字开头
-
-
2)代码运行没结果
-
原因:右键运行没有
unittest for xxx
的提示 而出现的问题 -
解决方案1:重新建一个代码文件,将写好的代码复制进去
-
解决方案2:删除已有的运行方式
-
- 3)没有找到用例:测试方法中不是以 test_ 开头的,或者单词写错了
步骤:
-
1)导包 import unittest
-
2)自定义测试类
-
3)在测试类中书写测试方法
-
4)执行用例
代码:
# 1、导包
import unittest# 2、新建测试类,需要继承 unittest 模块中的 TestCase类 即可
class TestDemo1(unittest.TestCase):# 3、书写测试方法(即 用例代码,目前没有真正的用例代码,使用 print 代替)# 注意:书写要求;测试方法 必须以 test_ 开头(本质以 test 开头)def test_method1(self):print("测试方法1")def test_method2(self):print("测试方法2")class TestDemo2(unittest.TestCase):def test_method3(self):print("测试方法3")def test_method4(self):print("测试方法4")# 4、执行用例(方法)
# 4.1 将光标放在 类名后边 运行,会执行类中的 所有测试方法
# 4.2 将光标放在 方法名后边 运行,只执行当前的方法
4.2 TestSuite & TestRunner(测试套件&运行)
介绍:
-
1)TestSuite(测试套件):管理、打包、组装 多个 TestCase(测试用例) 文件
-
2)TestRunner(测试执行):执行 TestSuite(测试套件)
步骤:
-
1)导包 import unittest
-
2)实例化套件对象(创建套件对象)
-
3)使用套件对象 添加 用例方法
-
4)实例化运行对象(创建运行对象)
-
5)使用运行对象 去执行 套件对象
实例化套件对象并添加测试用例:
# 导包
import unittest
from UnitTest_study.hm01_unittest import TestDemo# 实例化套件对象
suite = unittest.TestSuite()
# 方法1:单个添加测试用例(需要导包TestDemo)
suite.addTest(TestDemo('test_001'))
suite.addTest(TestDemo('test_002'))
# 方法2:批量添加测试用例(需要导包TestDemo)
suite.addTest(unittest.makeSuite(TestDemo))
实例化运行对象:
# 导包
import unittest
# 实例化运行对象
runner = unittest.TextTestRunner()
# 运行测试套件
runner.run(suite)
查看测试执行结果的方法:
- 说明: .运行通过;F用例不通过;E用例代码有问题
代码:
方法一:使用套件对象单个(单个方法)添加用例并运行
- 添加用例基本格式:unittest.TestSuite().addTest(类名('方法名'))
- 特点:逐条添加测试用例
- 运行用例基本格式:unittest.TextTestRunner().run(测试用例集)
# 1、导包
import unittest
from unittest_testcase import TestDemo1, TestDemo2# 2、实例化套件对象(创建套件对象)
suite = unittest.TestSuite()# 3、使用套件对象 添加 用例方法
suite.addTest(TestDemo1('test_method1'))
suite.addTest(TestDemo1('test_method2'))
suite.addTest(TestDemo2('test_method1'))
suite.addTest(TestDemo2('test_method2'))
# 4、实例化运行对象(创建运行对象)
runner = unittest.TextTestRunner()
# 5、使用运行对象 去执行 套件对象
# 格式:运行对象.run(套件对象)
runner.run(suite)
方法二:使用套件对象批量(单个类)添加用例并运行
- 添加用例基本格式:unittest.TestSuite().addTest(unittest.makeSuite(类名))
- 特点:批量添加测试用例
- 运行用例基本格式:unittest.TextTestRunner().run(测试用例集)
# 1、导包 import unittest from unittest_testcase import TestDemo1, TestDemo2# 2、实例化套件对象(创建套件对象) suite = unittest.TestSuite()# 3、使用套件对象 添加 用例方法 suite.addTest(unittest.makeSuite(TestDemo1)) suite.addTest(unittest.makeSuite(TestDemo2)) # 4、实例化运行对象(创建运行对象) runner = unittest.TextTestRunner() # 5、使用运行对象 去执行 套件对象 # 格式:运行对象.run(套件对象) runner.run(suite)
4.3 TestLoader(测试加载)
介绍: TestLoader(测试加载),和 TestSuite 的作用一样,是对 TestSuite 功能的补充,用来管理组装(打包)多个 TestCase。在一个项目中 TestCase(测试用例)的代码,一般放在一个单独的目录(case)中
步骤
- 1)导包 import unittest
- 2)实例化测试加载对象并添加用例(得到的是 suite 对象)
- 3)实例化 运行对象
- 4)运行对象执行套件对象
代码:
- 基本格式一:suite = unittest.TestLoader().discover('case', 'test_*.py')
- 基本格式二:suite = unittest.defaultTestLoader.discover('case', 'test_*.py')
# 导包
import unittest# 方法1:实例化对象并添加测试用例
suite = unittest.TestLoader().discover('case', 'test_*.py')
# 方法2:实例化对象并添加测试用例(说明:插件代码中有一句:defaultTestLoader = TestLoader(),所以有了方法2)
suite = unittest.defaultTestLoader.discover('case', 'test_*.py')
# 运行测试套件
unittest.TextTestRunner().run(suite)
4.4 Fixture(测试夹具)
介绍: Fixture(测试夹具)是一个代码结构,书写在 TestCase代码中,可以在每个方法执行前后都会执行的内容(在某些特定情况下会自动执行)。Fixture是一个概述,对一个测试用例环境的初始化和销毁就是一个Fixture。
Fixture控制级别: 方法级别、类级别、模块级别
方法级别【掌握】
作用:在每个测试方法(用例代码)执行前后都会自动调用的结构
关键字:setUp、tearDown
格式:
class TestDemo(object):"""测试示例类"""def setUp(self):"""每个方法执行之前都会执行"""print("方法 -> 开始")def tearDown(self):"""每个方法执行之后都会执行"""print("方法 -> 结束")
类级别【掌握】
-
作用:在每个测试类中所有方法执行前后 都会自动调用的结构(在整个类中 执行之前执行之后各一次)
-
说明:类级别的fixture 是一个 类方法,要用
@classmethod
来装饰 -
关键字:
setUpClass
、tearDownClass
-
格式:
class TestDemo(object):"""测试示例类"""@classmethoddef setUpClass(cls):"""每个方法执行之前都会执行"""print("类 -> 开始")@classmethoddef tearDownClass(cls):"""每个方法执行之后都会执行"""print("类 -> 结束")
模块级别【了解】
-
作用:在每个模块(代码文件)执行前后执行的代码结构
-
关键字:
setUpModule
、tearDownModule
-
格式:
# 模块级别的需要写在类的外边,直接定义函数即可
def setUpModule(self):"""每个方法执行之前都会执行"""print("模块 -> 开始")def tearDownModule(self):"""每个方法执行之后都会执行"""print("模块 -> 结束")class TestDemo(object):"""测试示例类"""
代码:
import unittestclass TestLogin(unittest.TestCase):def setUp(self) -> None:"""每个测试方法执行之前都会先调用的方法"""print('输入网址...')def tearDown(self) -> None:"""每个测试方法执行之后都会调用的方法"""print('关闭当前页面...')@classmethoddef setUpClass(cls) -> None:print('1、打开浏览器...')@classmethoddef tearDownClass(cls) -> None:print('...5、关闭浏览器')def test_01(self):print('输入正确用户名密码验证码,点击登录')def test_02(self):print('输入错误用户名密码验证码,点击登录')
4.5 跳过
介绍:
-
使用场景:对于一些未完成的或者不满足测试条件的测试函数和测试类,不想执行,可以使用跳过
-
使用方法:利用装饰器来完成,代码书写在 TestCase 文件中
-
格式一:直接将测试函数标记成跳过
-
@unittest.skip('跳过原因')
-
-
格式二:根据条件判断测试函数是否跳过,判断条件成立则跳过
-
@unittest.skipIf(判断条件, '跳过原因')
-
代码:
import unittest# version = 30
version = 29class TestDemo(unittest.TestCase):@unittest.skip('没有什么原因,就是不想执行')def test_01(self):print('测试方法01')@unittest.skipIf(version >= 30, '版本大于 30,不用测试')def test_02(self):print('测试方法02')def test_03(self):print('测试方法03')
4.6 断言
介绍:
-
概念:让程序代替人为判断测试程序执行结果是否符合预期结果的过程
-
作用:
-
-
1)提高写实效率
-
2)实现自动化测试(让脚本在无人值守状态下运行)
-
-
断言结果:
-
True,用例通过
-
False,代码抛出异常,用例不通过
-
-
说明:
-
1)UnitTest中提供了非常丰富的断言方法
-
2)复杂的断言方法在自动化测试中几乎使用不到,所以掌握几个常用的即可
-
3)在unittest中使用断言,都需要通过 self.断言方法 来使用
-
UnitTest断言方法:
self.assertEqual(预期结果, 实际结果)
-
含义:判断预期结果和实际结果 是否相等
-
返回结果:如果相等,用例通过;如果不相等,用例不通过,抛出异常。
self.assertIn(预期结果, 实际结果)
-
含义:判断预期结果 是否包含 在实际结果中
-
结果:包含,用例通过;不包含,用例不通过,抛出异常。
代码:
import unittestfrom UnitTest_study.hm07_login import loginclass TestLogin(unittest.TestCase):def test_uername_password_ok(self):"""正确用户名和密码:admin,123456,登录成功"""self.assertEqual('登录成功', login('admin', '123456'))
4.7 参数化
介绍:
-
好处:相似代码不需要多次书写
-
说明:
-
-
1)在测试方法中,使用 变量 来代替具体的测试数据,然后使用传参的方法将测试数据传递给方法的变量
-
2)unittest框架本身不支持 参数化,要使用参数化需要安装插件来完成
-
-
场景:
-
-
1)测试数据一般放在 json 文件中
-
2)使用代码读取 json 文件,提取出符合要求格式的数据—>>
[(), ()]
或[[], []]
-
-
安装:
pip install parameterized
-
步骤:
- 1)导包 unittest,parameterized
- 2)定义测试类
- 3)书写测试方法(用到的测试数据使用变量代替)
- 4)组织测试数据并传参
-
测试数据:
-
1)测试数据放在 json 文件中
-
2)新建函数读取 json文件中的 测试数据,读取的数据格式:
[(), (), ()]
或[[], [], []]
-
# 组织测试数据 [(), (), ()]
def build_data():with open('data.json', encoding='utf-8') as f:result = json.load(f) # [{}, {}, {}]data = []for i in result: # i -> {}data.append((i.get('username'), i.get('password'), i.get('expect'))) # [(), (), ()]return data
-
3)利用装饰器给函数变量传递数据
-
@parameterized.expand(build_data())
代码示例1:
import unittest
from parameterized import parameterized
from UnitTest_study.hm07_login import login# 组织测试数据 [(), (), ()]
data = [('admin', '123456', '登录成功'),('root', '123456', '登录失败'),('admin', '123123', '登录失败')
]# 定义测试类
class TestLogin(unittest.TestCase):# 书写测试方法(用到的测试数据使用变量代替)# 组织测试数据并传参(装饰器 @)@parameterized.expand(data)def test_login(self, username, password, expect):"""正确用户名和密码:admin,123456,登录成功"""self.assertEqual(expect, login(username, password))
代码示例2:
测试数据文件: data.json
代码:
import json
import unittest
from parameterized import parameterized
from UnitTest_study.hm07_login import login# 组织测试数据 [(), (), ()]
def build_data():with open('data.json', encoding='utf-8') as f:result = json.load(f) # [{}, {}, {}]data = []for i in result: # i -> {}data.append((i.get('username'), i.get('password'), i.get('expect'))) # [(), (), ()]return data# 定义测试类
class TestLogin(unittest.TestCase):# 书写测试方法(用到的测试数据使用变量代替)# 组织测试数据并传参(装饰器 @)@parameterized.expand(build_data())def test_login(self, username, password, expect):"""正确用户名和密码:admin,123456,登录成功"""self.assertEqual(expect, login(username, password))
4.8 测试报告插件
自带的测试报告:
只有单独运行 TestCase 的代码时,才会生成测试报告;位置在控制台左侧区域的右上角
第三方测试报告:
- 1)获取第三方的 测试运行类模块,并将其放在代码目录中
- 2)导包 unittest、HTMLTestRunner、HTMLTestReportCN
- 3)实例化 套件对象,并使用 加载套件加载用例
- 4)实例化 第三方的运行对象,运行 套件对象,并生成测试报告
HTMLTestRunner 插件:
- HTMLTestRunner
# 导包
import unittest
from UnitTest_study.HTMLTestRunner import HTMLTestRunnersuite = unittest.defaultTestLoader.discover('.', 'hm08_parameterized1.py')
file = 'report.html'
with open(file, 'wb') as f:runner = HTMLTestRunner(f, 2, '测试报告', 'python 3.7')runner.run(suite)
- HTMLTestRunnerCN
# 导包
import unittest
from UnitTest_study.HTMLTestRunnerCN import HTMLTestReportCNsuite = unittest.defaultTestLoader.discover('.', 'hm08_parameterized1.py')with open('report_CN.html', 'wb') as f:HTMLTestReportCN(f).run(suite)
4.9 总结
1)TestCase(测试用例):组织用例文件(TestCase)里面,书写 跳过、断言、参数化,使用Fixture夹具;单个测试文件直接运行,可以得到测试报告;有多个测试文件运行,则需要组装运行生成测试报告。
2、添加测试用例 到 测试套件中
- TestSuite(测试套件):套件对象组装测试用例
# 导包
import unittest
from UnitTest_study.hm01_unittest import TestDemo# 实例化套件对象
suite = unittest.TestSuite()
# 方法1:单个添加测试用例(需要导包TestDemo)
suite.addTest(TestDemo('test_001'))
suite.addTest(TestDemo('test_002'))
# 方法2:批量添加测试用例(需要导包TestDemo)
suite.addTest(unittest.makeSuite(TestDemo))
- TestLoader(测试加载):默认加载对象加载测试用例
# 导包
import unittest# 方法1:实例化对象并添加测试用例
suite = unittest.TestLoader().discover('case', 'hm*.py')
# 方法2:实例化对象并添加测试用例
suite = unittest.defaultTestLoader.discover('case', 'hm*.py')
# 运行测试套件
unittest.TextTestRunner().run(suite)
3、实例化运行对象 运行 测试条件
-
TestRunner(运行对象)
# 导包
import unittest
# 实例化运行对象
runner = unittest.TextTestRunner()
# 运行测试套件
runner.run(suite)
-
运行并生成测试报告
-
基本格式:
-
with open('./report.html', 'wb') as f:
-
# 实例化第三方运行对象
-
第三方运行对象 = 第三方运行类(f)
-
第三方运行对象.run(套件对象)
-
第三方运行类(文件对象(打开文件需要使用 wb 方式))
-
HTMLTestRunner
import unittest
from UnitTest_study.HTMLTestRunner import HTMLTestRunnersuite = unittest.defaultTestLoader.discover('.', 'hm08_parameterized1.py')
file = 'report.html'
with open(file, 'wb') as f:runner = HTMLTestRunner(f, 2, '测试报告', 'python 3.7')runner.run(suite)
- HTMLTestRunnerCN
import unittest
from UnitTest_study.HTMLTestRunnerCN import HTMLTestReportCNsuite = unittest.defaultTestLoader.discover('.', 'hm08_parameterized1.py')with open('report_CN.html', 'wb') as f:runner = HTMLTestReportCN(f)runner.run(suite)
二、PyTest框架介绍
1、什么是PyTest框架?
PyTest是python中一种单元测试框架,同自带的UnitTest测试框架类似,相比于UnitTest框架使用起来更简洁,效率更高。在自动化测试中充当测试执行的功能,并可以与UnitTest互换。
2、为什么使用PyTest框架?
- 1)非常容易上手,入门简单,文档丰富,文档中有很多实例可以参考,官方文档:https://docs.pytest.org/en/6.2.x/
- 2)支持简单的单元测试和复杂的功能测试
- 3)支持参数化,UnitTest需要通过插件扩展参数化功能
- 4)执行测试过程中可以将某些测试跳过,或者对某些预期失败的Case标记成失败
- 5)支持重复执行失败的Case:通过安装插件实现
- 6)支持运行由Nose,UnitTest编写的测试Case。注:pytest框架的脚本在UnitTest下无法执行
- 7)具有很多第三方插件,并且可以自定义扩展。插件获取:https://docs.pytest.org/en/latest/reference/plugin_list.html
- 8)方便的和持续集成工具集成
3、PyTest框架安装与基本使用
与UnitTest不同的是,pytest需要先安装才能使用。注意:如果pytest命令无法使用,则需要以管理员身份重新安装pytest。
安装步骤:
- 打开cmd窗口,输入命令:
- 安装:pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pytest
- 确认版本:pytest --version
- 查看:pip show pytest
测试形式:
-
测试函数形式
-
1)新建hm01.py文件,文件中编写函数名以test开头的测试函数
-
2)打开
Terminal
,切换到.py文件路径下,输入pytest -s hm01.py
-
3)控制台查看执行结果
-
测试类形式:
-
1)新建hm02.py文件,文件中编写类名以Test开头的测试类,测试方法名必须以 test 开头
-
2)打开
Terminal
,切换到.py文件路径下,输入pytest -s hm02.py
-
3)控制台查看执行结果
运行方式:
-
命令行模式【建议】
-
命令行中执行:
pytest -s 测试脚本文件
-
如:
pytest -s test_login.py
-
-
主函数模式
-
在 测试脚本文件中增加主函数:
-
# 主函数模式基本格式:
if __name__ = '__main__':pytest.main(["-s", "文件名.py"])
#举例:在 test_login.py 文件中增加主函数:
if __name__ = '__main__':pytest.main(["-s", "test_login.py"])
说明:
-
1)-s 表示支持控制台打印,如果不加,print不会出现任何内容
-
2)主函数模式需要导包 import pytest
4、PyTest框架实践
4.1 配置文件
介绍:
-
场景:使用配置文件后可以快速的使用配置的项来选择执行哪些测试模块
步骤:
-
1)项目下新建scripts模块
-
2)将测试脚本文件放到scripts中
-
3)pytest的配置文件放在自动化项目目录下
-
4)名称为pytest.ini
-
5)命令行运行时会使用该配置文件中的配置
-
6)第一行内容为
[pytest]
示例:
- [pytest] # 告知系统该文件的例行
- testpaths = ./case # testpaths指定测试用例存放位置
- addopts = -s # 添加pytest命令选项
- python_files = test*.py # 指定测试脚本文件名前缀
- python_classes = Test* # 指定测试类名前缀
- python_functions = test* # 指定测试函数/测试方法名前缀
注意:
- 1、在Windows系统下,pytest配置文件中,不允许写注释信息
- 2、一个工程内只需要一个 pytest.ini 配置文件,并且需要保证文件名正确
- 3、一般情况,只需要将 pytest.ini 配置文件 置于工程根目录下
- 4、配置有 pytest.ini 配置文件的工程,只需要打开命令行输入 pytest 命令,即可执行测试(注意:必须要cd切换到和配置文件同一级的目录下输入pytest)
默认配置:
- 说明:测试用例文件名/测试类名/测试方法名均为 Test/test开头(没有配置文件的默认规则)
- 代码:
- [pytest]
- testpaths = ./case
- addopts = -s
- python_files = test*.py
- python_classes = Test*
- python_functions = test*
自定义规则:
-
说明:测试用例文件名/测试类名/测试方法名,需要根据具体项目进行设置,以下以Hm/hm为例
-
扩展:指定单个文件/类/方法执行,只需要在配置文件中写死即可
代码:
-
[pytest]
-
testpaths = ./case
-
addopts = -s
-
python_files = hm*.py
-
python_classes = Hm*
-
python_functions = hm*
4.2 特殊方法
函数级别方法: setup和teardown
介绍:
场景:
-
pytest在运行自动化脚本的前后会执行两个特殊的方法,分别setup和teardown。
-
在执行脚本之前会执行setup方法,在执行脚本之后会执行teardown方法。
-
-
有了这两个方法,我们可以在setup中进行获取驱动对象的操作,在teardown中进行关闭驱动对象的操作。
-
-
说明:特殊方法名写法固定,没有代码提示,需要手写。
代码格式:
import pytestclass TestDemo(object):"""测试示例类"""def setup(self):"""开始方法"""print("方法 -> 开始")def teardown(self):"""结束方法"""print("方法 -> 结束")"""
举例:函数级别 setup
、teardown
"""
import pytest
class TestDemo(object):"""测试示例类"""# 说明:特殊方法名写法固定,没有代码提示,需要手写。def setup(self):"""开始方法"""print("函数 -> 开始")def teardown(self):"""结束方法"""print("函数 -> 结束")def test_method1(self):"""示例测试方法"""print("测试方法1")def test_method2(self):"""示例测试方法"""print("测试方法2")
if __name__ == '__main__':pytest.main(['-s', 'hm05_pytest.py'])
类级别方法: setup_class
和teardown_class
介绍: 运行于测试类的始末,在一个测试内只运行一次setup_class和teardown_class,不关心测试类内有多少个测试函数
代码格式:
import pytestclass TestDemo(object):"""测试示例类"""def setup_class(self):"""开始方法"""print("类 -> 开始")def teardown_class(self):"""结束方法"""print("类 -> 结束")"""
举例:类级别 setup_class
、teardown_class
"""
import pytest
class TestDemo(object):"""测试示例类"""# 说明:特殊方法名写法固定,没有代码提示,需要手写。def setup_class(self):"""开始方法"""print("类 -> 开始")def teardown_class(self):"""结束方法"""print("类 -> 结束")def test_method1(self):"""示例测试方法"""print("测试方法1")def test_method2(self):"""示例测试方法"""print("测试方法2")
if __name__ == '__main__':pytest.main(['-s', 'hm06_pytest.py'])
举例:函数级别和类级别同时使用
"""
特殊方法:函数级别和类级别同时使用
"""
import pytestclass TestDemo(object):"""测试示例类"""# 执行顺序:1 ->3 ->5 ->4 ->3 ->6 ->4 ->2def setup_class(self): # 1print("类级别 ->> 开始")def teardown_class(self): # 2print("类级别 ->> 结束")def setup(self): # 3print("函数级别 -> 开始")def teardown(self): # 4print("函数级别 -> 结束")def test_method1(self): # 5"""示例测试方法"""print("测试方法1")def test_method2(self): # 6"""示例测试方法"""print("测试方法2")if __name__ == '__main__':pytest.main(['-s', 'hm07_pytest.py'])
4.3 执行顺序插件
介绍:
-
场景:现实生活中,如果想下订单,必须先登录,我们可以通过插件的形式来控制函数执行的顺序。
-
安装:
pip3 install pytest-ordering
-
使用:
1)标记于被测试函数,@pytest.mark.run(order=x)
2)根据order传入的参数来解决运行顺序
3)order值全为正数或全为负数时,运行顺序:值越小,优先级越高
4)正数和负数同时存在:正数优先级高
5)控制方法执行顺序对测试类同样有效,使用方法一样
-
格式:
@pytest.mark.run(order=x)
代码:
"""
pytest 控制方法执行顺序插件
"""
import pytest@pytest.mark.run(order=2)
class TestDemo1(object):"""测试示例类"""# 语法:@pytest.mark.run(order=序号)# 注意:run(order=序号)没有代码提示,需要手写@pytest.mark.run(order=3)def test_method1(self):"""示例测试方法"""print("测试方法1")@pytest.mark.run(order=1)def test_method2(self):"""示例测试方法"""print("测试方法2")@pytest.mark.run(order=2)def test_method3(self):"""示例测试方法"""print("测试方法3")# 扩展:序号支持正数和负数,以及正负混合
# 1、纯正数:数越小,优先级越高【掌握】
# 2、纯负数:数越小,优先级越高【了解】
# 3、正负混合:正数先按照顺序执行,负数最后执行【了解】# 注意:控制方法执行顺序对测试类同样有效
@pytest.mark.run(order=1)
class TestDemo2(object):"""测试示例类"""def test_method(self):"""示例测试方法"""print("测试类2 -> 测试方法")if __name__ == '__main__':pytest.main(['-s', 'hm08_pytest.py'])
4.4 失败重试插件
介绍:
-
场景:自动化测试脚本可能会使用到网络,如果网络不好可能最终会使脚本不通过。像这种情况可能并不是脚本本身的问题,仅仅是因为网络忽快忽慢,那么我们可以使用失败重试的插件,当失败后尝试再次运行。一般情况最终成功可以视为成功,但最好进行排查看是否是脚本问题。
- 安装:pip3 install pytest-rerunfailures
- 使用:在配置文件中的命令行参数中增加 --reruns n
n:为失败重试几次
如:addopts = -s --html=./report/test_report.html --self-contained-html --reruns 3
-
说明:
-
1)正式脚本一般设置不超过3次,测试脚本设置1次即可
-
2)当脚本执行报错时,会自动重新执行设置次数
-
-
格式:
addopts = -s --reruns 3
代码: 配置文件
-
[pytest]
-
testpaths = ./case
-
addopts = -s --reruns 3
-
python_files = test*.py
-
python_classes = Test*
-
python_functions = test*
4.5 跳过
介绍:
- 场景:同一个软件在不同的设备上可能会有不同的效果,比如,iOS的3d touch操作是需要6s以上设备支持的,6和6s都可以安装同一款应用,如果设备不支持,那根本没有必要去测试这个功能,此时,可以让这种函数进行跳过。
- 说明:同样支持跳过测试类,使用方式一样
格式:@pytest.mark.skipif(condition, reason=None)
-
在需要跳过的测试脚本之上加上装饰器
-
condition
跳过的条件,必传参数;
-
-
reason
标注原因,必传参数 -
注意:
reason=
不能省略,否则会报错!
-
代码:
@pytest.mark.skipif(condition, reason='xxx')"""
pytest 跳过测试
"""
import pytestversion = 25 # 模拟软件版本号class TestDemo(object):"""测试示例类"""def test_method1(self):"""示例测试方法"""print("测试方法1")# 注意:reason= 不能省略,否则会报错!@pytest.mark.skipif(version >= 25, reason='当前版本不执行')def test_method2(self):"""示例测试方法"""print("测试方法2")def test_method3(self):"""示例测试方法"""print("测试方法3")# 说明:同样可以跳过测试类
@pytest.mark.skipif(version >= 25, reason='当前版本不执行')
class TestDemo2(object):"""测试示例类"""def test_method(self):"""示例测试方法"""print("测试类2->测试方法")if __name__ == '__main__':pytest.main(['-s', 'hm09_pytest.py'])
4.6 断言
Python自带断言:
-
预期相等:
assert 1 == 1
-
预期包含:
assert 'admin' in '欢迎 admin 归来!'
代码:
import pytestdef add_func(num1, num2):"""加法函数"""return num1 + num2class TestDemo(object):"""示例测试类"""# 调用被测函数result = add_func(1, 2)# 断言判断结果assert 3 == result
4.7 参数化
介绍:
-
应用场景:登录功能都是输入用户名,输入密码,点击登录。但登录的用户名和密码如果想测试多个值是没有办法用普通的操作实现的。数据参数化可以帮我实现这样的效果。
- 格式:@pytest.mark.parametrize(argnames, argvalues, indirect, ids=None, scope=None)
在需要参数化的测试脚本之上加上装饰器
argnames:参数名
argvalues:参数对应值,类型必须为可迭代类型,一般使用list
单个参数:
-
介绍:
-
1)argnames为字符串类型,根据需求决定合适的参数名
-
2)argvalues为列表类型,根据需求决定列表元素中的内容
-
-
3)在测试脚本中,参数名字与argnames保持一致
-
4)在测试脚本中正常使用
-
-
格式:
@pytest.mark.parametrize('参数变量', ['数值1', '数值2', ...])
-
代码:
-
"""
pytest 参数化功能:单个参数
"""
import pytestclass TestDemo(object):"""示例测试类"""# @pytest.mark.parametrize('参数变量', ['数值1', '数值2', ...])@pytest.mark.parametrize('name', ['小米', '小新'])def test_method1(self, name):"""示例测试方法"""print("获取的名字是:", name)if __name__ == '__main__':pytest.main(['-s', 'hm10_pytest.py'])
多个参数:
-
介绍:
-
1)多个参数必须置于同一个字符串内!
-
2)数据格式必须是:
[(),()]
或者[[], []]
-
-
格式一:@pytest.mark.parametrize('参数1, 参数n', [('数值1-1', '数值2-2'), ('数值2-1', '数值2-2'), ...])
格式二:@pytest.mark.parametrize(('参数1', '参数n'), [('数值1-1', '数值2-2'), ('数值2-1', '数值2-2'), ...])
代码示例1:
""" pytest 参数化功能:多个参数 """ import pytestclass TestDemo(object):"""示例测试类"""@pytest.mark.parametrize('name, pwd', [('admin', 123456), ('test', 654321)])def test_method1(self, name, pwd):"""示例测试方法"""print("账号:{} 的秘密是:{}".format(name, pwd))if __name__ == '__main__':pytest.main(['-s', 'hm11_pytest.py'])
代码示例2:
import pytestdef build_test_data():"""构造测试数据函数"""# 中间代码略return [('admin', 123456), ('test', 654321), ('xxx', 'yyy')]class TestDemo(object):"""示例测试类"""# 通过方法引入数据,pytest中必须带小括号@pytest.mark.parametrize('name, pwd', build_test_data())def test_method1(self, name, pwd):"""示例测试方法"""print("账号:{} 的秘密是:{}".format(name, pwd))
4.8 测试报告插件
介绍:
- 应用场景:自动化测试脚本最终执行是通过还是不通过,需要通过测试报告进行体现。
- 安装:pip3 install pytest-html
- 使用:在配置文件中的命令行参数中增加 --html=用户路径/report.html
- 扩展:在 --html 后面加上 --self-contained-html,可以把CSS样式内嵌到html报告文件中(防止分享报告时丢失样式)
示例:
-
addopts = -s --html=./report/test_report.html
-
addopts = -s --html=./report/test_report.html --self-contained-html
-
格式:
addopts = -s --html=./report/test_report.html --self-contained-html
代码: 配置文件
-
[pytest]
-
testpaths = ./case
- addopts = -s --html=./report/test_report.html --self-contained-html
- python_files = test*.py
- python_classes = Test*
- python_functions = test*
4.9 PyTest框架运行UnitTest的TestCase
在配置文件中把python_files的名字修改为TestCase测试用例的文件名(xxx.py)即可。
4.10 总结
配置文件:
-
位置:
项目/pytest.ini
-
内容:
-
[pytest]
-
- testpaths = ./case
- addopts = -s --html=./report/test_report.html --self-contained-html --reruns 3
- python_files = test*.py
- python_classes = Test*
- python_functions = test*
【最后可能给予你助力的自动化教程】
最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走
关注公众号”程序员万金油“免费
面试资料
我们学习软件测试必然是为了找到高薪的工作,下面这些面试题是来自阿里、腾讯、字节等一线互联网大厂最新的面试资料,并且有阿里大佬给出了权威的解答,刷完这一套面试资料相信大家都能找到满意的工作。
上面是我整理的配套资源,这些资源对于软件测试的的朋友来说应该是最全面最完整的备战仓库,为了更好地整理每个模块,我也参考了很多网上的优质博文和项目,力求不漏掉每一个知识点,很多朋友靠着这些内容进行复习,拿到了BATJ等大厂的offer,这个仓库也已经帮助了很多的软件测试的学习者,希望也能帮助到你。